Xamarin 中的 IntentService PCL 解决方案
IntentService in Xamarin PCL Solution
我正忙着编写一个应用程序,用户需要在其中捕获大量图像,然后将它们与一些文本数据打包在一起,然后将它们上传到本地服务器。我想通过 Intent Service 在 Android 平台上实现上传,但我找不到好的 Xamarin Forms PCL 示例来展示如何。
这是我初始化要传递给 IntentService 的 Intent 的方法:
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: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
在您的 Intent
声明中,您需要告诉您要调用的服务
像这样:
var uploadIntent = new Intent(this, typeof(ServiceIntent));
注:this
代表Context
.
更新:
如评论中所述,您的接口实现不能派生自 Activity
class。为了能够访问 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 安装,设置非常简单。基本上,它可以让您访问当前 activity,您可以将其用作调用 IntentService
的上下文
希望对您有所帮助。-
这里是 IntentService.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
在Android中,我们通常使用IntentService
来做异步操作。众所周知,线程也是用来做异步操作的。 IntentService
和Thread
的区别是IntentService
是Service
,属于Android组件。所以,IntentService
的优先级高于Thread.
例如,有一个 ActivityA
有一个 IntentService
,有一个 ActivityB
有一个 Thread
,IntentService
和Thread
正在工作,ActivityA
和 ActivityB
都是背景 Activity
。现在,如果你的 phone 的系统没有额外的资源,你的 ActivityB
将首先被杀死。
关于异常:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
也就是说你应该使用android.content.Context
来调用StartService
方法。在Android中,一共有三种Context
。 Application, Activity and Service。所以可以直接调用这三个class中的StartService
方法。如果你不在这三个class中,你需要将Context
传递给你的class,然后使用Context
调用StartService
。
I added Activity for this class' inheritance.
如果你这样做,你的 class 将是一个 Activity
,你需要在你的清单中注册它,为你的 class 添加布局,它应该有lifecycle等等,不会是你想得到的class。在Android中,Activity是一个组件,不是普通的class,所以你不能继承它,除非你想让你的class成为一个Activity。
演示:
我给你做了一个demo,
我正忙着编写一个应用程序,用户需要在其中捕获大量图像,然后将它们与一些文本数据打包在一起,然后将它们上传到本地服务器。我想通过 Intent Service 在 Android 平台上实现上传,但我找不到好的 Xamarin Forms PCL 示例来展示如何。
这是我初始化要传递给 IntentService 的 Intent 的方法:
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: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
在您的 Intent
声明中,您需要告诉您要调用的服务
像这样:
var uploadIntent = new Intent(this, typeof(ServiceIntent));
注:this
代表Context
.
更新:
如评论中所述,您的接口实现不能派生自 Activity
class。为了能够访问 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 安装,设置非常简单。基本上,它可以让您访问当前 activity,您可以将其用作调用 IntentService
希望对您有所帮助。-
这里是 IntentService.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
在Android中,我们通常使用IntentService
来做异步操作。众所周知,线程也是用来做异步操作的。 IntentService
和Thread
的区别是IntentService
是Service
,属于Android组件。所以,IntentService
的优先级高于Thread.
例如,有一个 ActivityA
有一个 IntentService
,有一个 ActivityB
有一个 Thread
,IntentService
和Thread
正在工作,ActivityA
和 ActivityB
都是背景 Activity
。现在,如果你的 phone 的系统没有额外的资源,你的 ActivityB
将首先被杀死。
关于异常:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
也就是说你应该使用android.content.Context
来调用StartService
方法。在Android中,一共有三种Context
。 Application, Activity and Service。所以可以直接调用这三个class中的StartService
方法。如果你不在这三个class中,你需要将Context
传递给你的class,然后使用Context
调用StartService
。
I added Activity for this class' inheritance.
如果你这样做,你的 class 将是一个 Activity
,你需要在你的清单中注册它,为你的 class 添加布局,它应该有lifecycle等等,不会是你想得到的class。在Android中,Activity是一个组件,不是普通的class,所以你不能继承它,除非你想让你的class成为一个Activity。
演示:
我给你做了一个demo,