Xamarin PCL解决方案中的IntentService

时间:2018-04-09 14:19:18

标签: xamarin xamarin.forms xamarin.android intentservice

我正忙于编写一个应用程序,用户需要捕获大量图像,然后将它们与一些文本数据打包在一起,然后将它们上传到本地服务器。我想通过Intent Service在Android平台上实现上传,但我找不到一个好的Xamarin Forms PCL示例来向我展示如何。

这是我初始化Intent以传递给IntentService的方法:

public async Task<bool> UploadAsync(Uri serviceAddress, 
                       CaptureEntity capture, 
                       List<ImageEntity> images)
    {
        try
        {
            Intent uploadIntent = new Intent();
            uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
            uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
            StartService(uploadIntent);

            return true;
        }
        catch (Exception exc)
        {
            App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
            throw exc;
        }
    }

这是IntentService本身。

[Service]
public class ServiceIntent : IntentService
{
    public ServiceIntent() : base("ServiceIntent")
    {

    }

    //[return: GeneratedEnum]
    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        return base.OnStartCommand(intent, flags, startId);
    }

    public override void OnCreate()
    {
        base.OnCreate();
    }

    protected override void OnHandleIntent(Intent intent)
    {
        Uri serviceAddress = new Uri(intent.GetStringExtra("serviceAddress"));
        Guid captureId = Guid.Parse(intent.GetStringExtra("captureId"));
        CaptureEntity capture = new DatabaseConnection_Android().CreateConnection().Query<CaptureEntity>("SELECT * FROM [CaptureEntity]").Single(c => c.WorkflowId == captureId);
        var images = new DatabaseConnection_Android().CreateConnection().Query<ImageEntity>("SELECT * FROM [ImageEntity]").Where(i => i.CaptureEntityId == capture.Id);
        try
        {
            MultipartFormDataContent content = new MultipartFormDataContent();
            StringContent strContent = new StringContent(
                                        capture.XmlData,
                                        Encoding.UTF8,
                                        "text/xml");
            IImageHandler handler = new ImageHandler_Droid();

            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Add("workflow", capture.WorkflowId.ToString());
            request.Method = HttpMethod.Post;
            request.RequestUri = serviceAddress;

            foreach (var image in images)
            {
                byte[] imageByte = handler.ReadAllBytes(image.ImagePath);
                ByteArrayContent byteContent = new ByteArrayContent(imageByte);
                byteContent.Headers.Add("Content-Type", "image/jpeg");
                content.Add(byteContent, "file", image.ImageName);
            }
            content.Add(strContent, "text/xml");

            request.Content = content;

            using (HttpClient client = new HttpClient())
            {
                client.Timeout = TimeSpan.FromSeconds(180);

                var response = client.SendAsync(
                                            request,
                                            HttpCompletionOption.ResponseContentRead).Result;

                var readResponse = response.Content.ReadAsStringAsync().Result;

                if (readResponse == "File uploaded.")
                    MessagingCenter.Send<CaptureEntity, string>(
                        capture,
                        "Completed",
                        "Success");
                else if (readResponse.Contains("An error has occurred."))
                    MessagingCenter.Send<CaptureEntity, string>(
                        capture,
                        "Uploader",
                        String.Format(CultureInfo.InvariantCulture,
                        "Failed: {0}",
                        readResponse));
                else
                    MessagingCenter.Send<CaptureEntity, string>(
                        capture,
                        "Uploader",
                        String.Format(CultureInfo.InvariantCulture,
                        "Failed: {0}",
                        readResponse));
            }
        }
        catch (WebException webExc)
        {
            MessagingCenter.Send<string, string>("Uploader", "Failed",
                        String.Format(CultureInfo.InvariantCulture,
                        "{0} upload failed.\n{1}",
                        capture.DisplayName,
                        webExc.Message));
        }
        catch (TimeoutException timeExc)
        {
            MessagingCenter.Send<string, string>("Uploader", "Failed",
                        String.Format(CultureInfo.InvariantCulture,
                        "{0} upload failed.\n{1}",
                        capture.DisplayName,
                        timeExc.Message));
        }
        catch (Exception exc)
        {
            MessagingCenter.Send<string, string>("Uploader", "Failed",
                        String.Format(CultureInfo.InvariantCulture,
                        "{0} upload failed.\n{1}",
                        capture.DisplayName,
                        exc.Message));
        }
    }
}

任何人都可以告诉我我做错了什么,因为当我想要启动服务时出现以下错误:

  

Java.Lang.NullPointerException:尝试在空对象引用上调用虚方法'android.content.ComponentName android.content.Context.startService(android.content.Intent)'

2 个答案:

答案 0 :(得分:1)

Intent声明中,您需要告诉您要拨打的服务

这样的事情:

var uploadIntent = new Intent(this, typeof(ServiceIntent));

注意:this代表Context

<强>更新

正如评论中所提到的,您的接口实现无法从Activity类派生。要访问Context以便能够调用StartService方法并创建Intent,您可以通过两种方式进行操作:

使用Xamarin.Forms.Forms.Context

public async Task<bool> UploadAsync(Uri serviceAddress, 
                    CaptureEntity capture, 
                    List<ImageEntity> images)
    {
        try
        {
            var context = Xamarin.Forms.Forms.Context;
            var uploadIntent = new Intent(context, typeof(ServiceIntent));
            uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
            uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
            context.StartService(uploadIntent);

            return true;
        }
        catch (Exception exc)
        {
            App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
            throw exc;
        }
    }

如果您使用的是最新版本的Xamarin.Forms,则不推荐使用此全局上下文,而是建议您使用本地上下文。您仍然可以使用它,但在将来的XF更新中,您的应用可能会中断。

使用 CurrentActivity plugin

public async Task<bool> UploadAsync(Uri serviceAddress, 
                    CaptureEntity capture, 
                    List<ImageEntity> images)
    {
        try
        {
            var context = CrossCurrentActivity.Current.Activity;
            var uploadIntent = new Intent(context, typeof(ServiceIntent));
            uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
            uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
            context.StartService(uploadIntent);

            return true;
        }
        catch (Exception exc)
        {
            App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
            throw exc;
        }
    }

这个插件可以从块中安装,设置非常简单。基本上,它允许您访问当前活动,您可以将其用作上下文来调用IntentService

希望这会有所帮助.-

答案 1 :(得分:0)

这是IntentService

  

IntentService是服务的基类,可根据需要处理异步请求(表示为Intents)。客户端通过startService(Intent)调用发送请求;服务根据需要启动,使用工作线程依次处理每个Intent,并在工作失败时自行停止。

在Android中,我们通常使用IntentService来执行异步操作。众所周知,线程也用于做异步操作。 IntentServiceThread IntentService之间的差异是Service,属于Android组件。因此,IntentService的优先级高于Thread.

例如,ActivityA有一个IntentService,而ActivityB有一个ThreadIntentService和{{} 1}}正在运行,ThreadActivityA都是背景ActivityB。现在,如果您的手机系统没有额外资源,您的Activity将首先被杀死。

关于例外:

  

Java.Lang.NullPointerException:尝试在空对象引用上调用虚方法'android.content.ComponentName android.content.Context.startService(android.content.Intent)'

这意味着您应该使用ActivityB来调用android.content.Context方法。在Android中,有三种StartServiceApplicationActivityService。因此,您可以直接在这三个类中调用Context方法。如果您不在这三个课程中,则需要将StartService传递给您的课程,然后使用Context致电Context

  

我为此类的继承添加了Activity。

如果你这样做,你的班级将是StartService,你需要在你的课堂上注册它,为你的班级添加布局,它应该有生命周期,等等。它不会是什么你想上课。在Android中,Activity是一个Component,而不是普通的类,因此除非您希望您的类成为Activity,否则不能继承它。

演示:

我为你做了demo