Android 使用 Volley plus 或 OkHttp 更新来自服务的通知进度
Android update notification progress from service using Volley plus or OkHttp
我需要你的帮助!我正在尝试更新回调文件上传过程状态栏中的进度条。调用更新进度条接口时尝试了两个,两个网络库都导致系统卡顿。我理解这意味着基本上回调过程在主线程中工作,无论我 运行 它们在服务中是什么。如何正确调用更新进度条,才不会导致系统UI卡顿?
注意:如果我在主线程 (Activity) 中更新通知,一切正常!两个示例都正常工作,服务器按预期接收文件。
服务中的 OkHTTP 代码
mUBuilder.setContentTitle("Upload image")
.setContentText("")
.setAutoCancel(false)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_launcher);
try {
final File file = new File(IMAGE_PATH);
final long totalSize = file.length();
RequestBody requestBody = new MultipartBody.Builder()
.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\"; filename=\"" + file.getName() + "\""),
new CountingFileRequestBody(file, "image/*", new CountingFileRequestBody.ProgressListener() {
@Override
public void transferred(long num) {
final float progress = (num / (float) totalSize) * 100;
Log.d(TAG, "OUT THREAD: " + progress); //see in logs
new Thread(
new Runnable() {
@Override
public void run() {
Log.d(TAG, "IN THREAD: " + progress); //not visible in the logs
mUBuilder.setProgress(100,(int) progress, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
}
);
}
}))
.build();
Request request = new Request.Builder()
.url("http://posttestserver.com/post.php?dir=123")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
mUBuilder.setContentTitle("Upload complete!");
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
System.out.println(response.body().string());
}
});
}catch(Exception e){
e.printStackTrace();
}
CountingFileRequestBody.java 从这个
Volley Plus 代码
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationIntent = new Intent(getApplicationContext(), NMainActivity.class);
final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext());
mBuilder.setContentTitle("Upload image")
.setContentText("")
.setAutoCancel(false)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_launcher);
SimpleMultiPartRequest jsonRequest = new SimpleMultiPartRequest(Request.Method.POST, "http://posttestserver.com/post.php?dir=123",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.i(getClass().getName(), response);
mBuilder.setContentText("Upload image complete!");
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mBuilder.build());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(getClass().getName(), error.toString());
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return mParams;
}
};
jsonRequest.addFile("images", (IMAGE_PATH);
jsonRequest.setFixedStreamingMode(true);
jsonRequest.setShouldCache(false);
jsonRequest.setShouldRetryServerErrors(true);
jsonRequest.setOnProgressListener(new Response.ProgressListener() {
@Override
public void onProgress(long transferredBytes, long totalSize) {
final int percentage = (int) ((transferredBytes / ((float) totalSize)) * 100);
mBuilder.setProgress(100, percentage, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mBuilder.build());
//freeze system UI
}
});
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
mRequestQueue.add(jsonRequest);
mRequestQueue.start();
我找到了解决这个问题的简单方法!
原文在这里
稍微修复一下 OkHTTP 的代码:
@Override
public void transferred(final int progress) {
handler.post(new Runnable() {
@Override
public void run() {
mUBuilder.setProgress(100, progress, true);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
});
}
CountingFileRequestBody.java
public class CountingFileRequestBody extends RequestBody {
...
int latestPercentDone, percentDone;
...
@Override
public void writeTo(BufferedSink sink) throws IOException {
...
while ((read = source.read(sink.buffer(), SEGMENT_SIZE)) != -1) {
total += read;
sink.flush();
latestPercentDone = (int) ((total / (float) file.length()) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
this.listener.transferred(percentDone);
}
}
}
public interface ProgressListener {
void transferred(int num);
}
}
Volley Plus 代码:
jsonRequest.setOnProgressListener(new Response.ProgressListener() {
int latestPercentDone, percentDone;
@Override
public void onProgress(long transferredBytes, long totalSize) {
latestPercentDone = (int) ((transferredBytes / (float) totalSize) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
mUBuilder.setProgress(100, percentDone, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
}
});
您也可以使用此代码:
final File file = new File(event.getPath().getPath());
final long totalSize = file.length();
new AsyncTask<Void, Integer, String>() {
@Override
protected void onProgressUpdate(Integer... values) {
mUBuilder.setProgress(100, values[0], false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
@Override
protected String doInBackground(Void... voids) {
try{
final File file = new File(event.getPath().getPath());
final long totalSize = file.length();
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://posttestserver.com/post.php?dir=123");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
FileBody fb = new FileBody(file);
builder.addPart("file", fb);
final HttpEntity yourEntity = builder.build();
class ProgressiveEntity implements HttpEntity {
@Override
public void consumeContent() throws IOException {
yourEntity.consumeContent();
}
@Override
public InputStream getContent() throws IOException,
IllegalStateException {
return yourEntity.getContent();
}
@Override
public Header getContentEncoding() {
return yourEntity.getContentEncoding();
}
@Override
public long getContentLength() {
return yourEntity.getContentLength();
}
@Override
public Header getContentType() {
return yourEntity.getContentType();
}
@Override
public boolean isChunked() {
return yourEntity.isChunked();
}
@Override
public boolean isRepeatable() {
return yourEntity.isRepeatable();
}
@Override
public boolean isStreaming() {
return yourEntity.isStreaming();
}
@Override
public void writeTo(OutputStream outstream) throws IOException {
class ProxyOutputStream extends FilterOutputStream {
public ProxyOutputStream(OutputStream proxy) {
super(proxy);
}
public void write(int idx) throws IOException {
out.write(idx);
}
public void write(byte[] bts) throws IOException {
out.write(bts);
}
public void write(byte[] bts, int st, int end) throws IOException {
out.write(bts, st, end);
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
}
class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
int latestPercentDone, percentDone;
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
totalSent = 0;
}
public void write(byte[] bts, int st, int end) throws IOException {
totalSent += end;
latestPercentDone = (int) ((totalSent / (float) totalSize) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
publishProgress(percentDone);
}
out.write(bts, st, end);
}
}
yourEntity.writeTo(new ProgressiveOutputStream(outstream));
}
}
ProgressiveEntity myEntity = new ProgressiveEntity();
post.setEntity(myEntity);
HttpResponse response = client.execute(post);
Log.d(TAG, response.toString());
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}.execute();
如果您使用最后一个示例代码,请不要忘记添加:
android {
useLibrary 'org.apache.http.legacy'
}
dependencies {
...
compile('org.apache.httpcomponents:httpcore:+') {
exclude module: "httpclient"
}
compile('org.apache.httpcomponents:httpmime:4.3.6') {
exclude module: "httpclient"
}
...
}
在 Android 6.0.1.
上测试
我需要你的帮助!我正在尝试更新回调文件上传过程状态栏中的进度条。调用更新进度条接口时尝试了两个,两个网络库都导致系统卡顿。我理解这意味着基本上回调过程在主线程中工作,无论我 运行 它们在服务中是什么。如何正确调用更新进度条,才不会导致系统UI卡顿?
注意:如果我在主线程 (Activity) 中更新通知,一切正常!两个示例都正常工作,服务器按预期接收文件。
服务中的 OkHTTP 代码
mUBuilder.setContentTitle("Upload image")
.setContentText("")
.setAutoCancel(false)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_launcher);
try {
final File file = new File(IMAGE_PATH);
final long totalSize = file.length();
RequestBody requestBody = new MultipartBody.Builder()
.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\"; filename=\"" + file.getName() + "\""),
new CountingFileRequestBody(file, "image/*", new CountingFileRequestBody.ProgressListener() {
@Override
public void transferred(long num) {
final float progress = (num / (float) totalSize) * 100;
Log.d(TAG, "OUT THREAD: " + progress); //see in logs
new Thread(
new Runnable() {
@Override
public void run() {
Log.d(TAG, "IN THREAD: " + progress); //not visible in the logs
mUBuilder.setProgress(100,(int) progress, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
}
);
}
}))
.build();
Request request = new Request.Builder()
.url("http://posttestserver.com/post.php?dir=123")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
mUBuilder.setContentTitle("Upload complete!");
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
System.out.println(response.body().string());
}
});
}catch(Exception e){
e.printStackTrace();
}
CountingFileRequestBody.java 从这个
Volley Plus 代码
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationIntent = new Intent(getApplicationContext(), NMainActivity.class);
final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext());
mBuilder.setContentTitle("Upload image")
.setContentText("")
.setAutoCancel(false)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_launcher);
SimpleMultiPartRequest jsonRequest = new SimpleMultiPartRequest(Request.Method.POST, "http://posttestserver.com/post.php?dir=123",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.i(getClass().getName(), response);
mBuilder.setContentText("Upload image complete!");
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mBuilder.build());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(getClass().getName(), error.toString());
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return mParams;
}
};
jsonRequest.addFile("images", (IMAGE_PATH);
jsonRequest.setFixedStreamingMode(true);
jsonRequest.setShouldCache(false);
jsonRequest.setShouldRetryServerErrors(true);
jsonRequest.setOnProgressListener(new Response.ProgressListener() {
@Override
public void onProgress(long transferredBytes, long totalSize) {
final int percentage = (int) ((transferredBytes / ((float) totalSize)) * 100);
mBuilder.setProgress(100, percentage, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mBuilder.build());
//freeze system UI
}
});
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
mRequestQueue.add(jsonRequest);
mRequestQueue.start();
我找到了解决这个问题的简单方法!
原文在这里
稍微修复一下 OkHTTP 的代码:
@Override
public void transferred(final int progress) {
handler.post(new Runnable() {
@Override
public void run() {
mUBuilder.setProgress(100, progress, true);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
});
}
CountingFileRequestBody.java
public class CountingFileRequestBody extends RequestBody {
...
int latestPercentDone, percentDone;
...
@Override
public void writeTo(BufferedSink sink) throws IOException {
...
while ((read = source.read(sink.buffer(), SEGMENT_SIZE)) != -1) {
total += read;
sink.flush();
latestPercentDone = (int) ((total / (float) file.length()) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
this.listener.transferred(percentDone);
}
}
}
public interface ProgressListener {
void transferred(int num);
}
}
Volley Plus 代码:
jsonRequest.setOnProgressListener(new Response.ProgressListener() {
int latestPercentDone, percentDone;
@Override
public void onProgress(long transferredBytes, long totalSize) {
latestPercentDone = (int) ((transferredBytes / (float) totalSize) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
mUBuilder.setProgress(100, percentDone, false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
}
});
您也可以使用此代码:
final File file = new File(event.getPath().getPath());
final long totalSize = file.length();
new AsyncTask<Void, Integer, String>() {
@Override
protected void onProgressUpdate(Integer... values) {
mUBuilder.setProgress(100, values[0], false);
mNotifyManager.notify(AppSettings.SEND_DATA_NOTIF, mUBuilder.build());
}
@Override
protected String doInBackground(Void... voids) {
try{
final File file = new File(event.getPath().getPath());
final long totalSize = file.length();
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://posttestserver.com/post.php?dir=123");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
FileBody fb = new FileBody(file);
builder.addPart("file", fb);
final HttpEntity yourEntity = builder.build();
class ProgressiveEntity implements HttpEntity {
@Override
public void consumeContent() throws IOException {
yourEntity.consumeContent();
}
@Override
public InputStream getContent() throws IOException,
IllegalStateException {
return yourEntity.getContent();
}
@Override
public Header getContentEncoding() {
return yourEntity.getContentEncoding();
}
@Override
public long getContentLength() {
return yourEntity.getContentLength();
}
@Override
public Header getContentType() {
return yourEntity.getContentType();
}
@Override
public boolean isChunked() {
return yourEntity.isChunked();
}
@Override
public boolean isRepeatable() {
return yourEntity.isRepeatable();
}
@Override
public boolean isStreaming() {
return yourEntity.isStreaming();
}
@Override
public void writeTo(OutputStream outstream) throws IOException {
class ProxyOutputStream extends FilterOutputStream {
public ProxyOutputStream(OutputStream proxy) {
super(proxy);
}
public void write(int idx) throws IOException {
out.write(idx);
}
public void write(byte[] bts) throws IOException {
out.write(bts);
}
public void write(byte[] bts, int st, int end) throws IOException {
out.write(bts, st, end);
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
}
class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
int latestPercentDone, percentDone;
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
totalSent = 0;
}
public void write(byte[] bts, int st, int end) throws IOException {
totalSent += end;
latestPercentDone = (int) ((totalSent / (float) totalSize) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
publishProgress(percentDone);
}
out.write(bts, st, end);
}
}
yourEntity.writeTo(new ProgressiveOutputStream(outstream));
}
}
ProgressiveEntity myEntity = new ProgressiveEntity();
post.setEntity(myEntity);
HttpResponse response = client.execute(post);
Log.d(TAG, response.toString());
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}.execute();
如果您使用最后一个示例代码,请不要忘记添加:
android {
useLibrary 'org.apache.http.legacy'
}
dependencies {
...
compile('org.apache.httpcomponents:httpcore:+') {
exclude module: "httpclient"
}
compile('org.apache.httpcomponents:httpmime:4.3.6') {
exclude module: "httpclient"
}
...
}
在 Android 6.0.1.
上测试