【问题标题】:IntentService in Xamarin PCL SolutionXamarin PCL 解决方案中的 IntentService
【发布时间】:2018-04-09 14:19:18
【问题描述】:

我正忙于编写一个应用程序,用户需要捕获大量图像,然后将它们与一些文本数据打包在一起,然后将它们上传到本地服务器。我想通过 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)”

【问题讨论】:

    标签: xamarin xamarin.forms xamarin.android intentservice


    【解决方案1】:

    在您的Intent 声明中,您需要告诉您要调用的服务

    类似这样的:

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

    注意:this 代表Context

    更新:

    如 cmets 中所述,您的接口实现不能派生自 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;
            }
        }
    

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

    希望这会有所帮助。-

    【讨论】:

    • 感谢@apineda,但是当我初始化 Intent:Java.Lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName( )' 在空对象引用上
    • uploadAsync 方法是通过依赖注入从我的 PCL 库中的接口调用的。我为这个类的继承添加了 Activity。
    • 这就是问题所在:您的班级不是Activity。活动被 Android 操作系统夸大时,它们以特殊方式初始化。获取当前活动上下文(您可以使用此包github.com/jamesmontemagno/CurrentActivityPlugin)并将其注入到您的 UploadService(不是 IntentService 而是调用它的那个),然后您可以调用 currentActivity.StartService(intent);如果您在这里卡住更新,我可能会提供帮助
    • @GANDA1F 刚刚添加了一个您可能会觉得有用的更新。
    【解决方案2】:

    这里是IntentService

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

    在Android中,我们通常使用IntentService来做异步操作符。众所周知,线程也是用来做异步操作的。 IntentServiceThread的区别是IntentServiceService,属于Android Component。所以IntentService的优先级高于Thread.

    例如,有一个ActivityA 有一个IntentService,有一个ActivityB 有一个ThreadIntentServiceThread 都在工作,ActivityAActivityB 是背景Activity。现在,如果您的手机系统没有多余的资源,您的ActivityB 将首先被杀死。

    关于异常:

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

    这意味着您应该使用android.content.Context 来调用StartService 方法。在Android中,Context有3种。 ApplicationActivityService。所以可以直接调用这三个类中的StartService方法。如果您不在这三个班级,则需要将Context 传递给您的班级,然后使用Context 调用StartService

    我为这个类的继承添加了 Activity。

    如果你这样做,你的类将是一个Activity,你需要在你的清单中注册它,为你的类添加布局,它应该有生命周期等等。它不会是你想要的上课。在Android中,Activity是一个组件,不是普通的类,所以除非你想让你的类成为一个Activity,否则你不能继承它。

    演示:

    我为你做了一个demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 2014-05-06
      • 2013-07-22
      • 2016-02-07
      • 1970-01-01
      相关资源
      最近更新 更多