I have written a native code for flutter that will download file from given url. Everything works fine and file will download and store perfectly. The problem is that when i want to retrieve downloaded percentage(download progress). I used event channel. It will work until I tap download button. After I tap download button nothing will retrieve from event channel until the download is finish. The download is a class that implement asyncTask. Even the onProgressUpdate method in asyncTask is unavailable until the download is complete. looks like the only and only one place that I can see progress and that is inside the doInBackGround method. I see the percentage by logs. I used event.success(percentage) inside while loop to retrieve percentage but got an error that said:
Methods marked with @UiThread must be executed on the main thread
Then used a Handler with mainloop but nothing happened.
and this is where i'm stuck and don't know what to do.
I appreciate any help.
Here is the codes:
Android Side: (MainActivity)
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "bot/native_services";
private static final String STREAM = "bot/native_services/stream";
private Download download = new Download();
static int percentage = 0;
static boolean dlStart = false;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
EventChannel.StreamHandler handler = new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
Log.i("MyTestApp","Registered");
Handler h = new Handler(Looper.getMainLooper());
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
h.post(new Runnable() {
@Override
public void run() {
events.success(percentage);
if (percentage == 100) {
events.endOfStream();
}
}
});
}
};
Timer timer = new Timer();
timer.schedule(timerTask,0,100);
if (percentage == 100) {
timer.cancel();
}
}
@Override
public void onCancel(Object arguments) {
}
};
new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),STREAM).setStreamHandler(handler);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),CHANNEL).setMethodCallHandler((call, result) -> {
if (call.method.equals("openUrl")) {
openUrl(call.argument("url"), result);
}else if(call.method.equals("checkNetwork")) {
checkNetwork(result);
}else if(call.method.equals("downloadFromUrl")) {
downloadFromUrl(call.argument("url"), result);
}
});
}
public void downloadFromUrl(String url,MethodChannel.Result result) {
download.execute(url);
try {
HashMap<String,String> res = download.get();
Log.i("MyTestApp",res.get("status"));
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Android side: (Download class)
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import javax.net.ssl.HttpsURLConnection;
import io.flutter.plugin.common.EventChannel;
class Download extends AsyncTask<String,Integer, HashMap<String,String>>{
String path = "";
@Override
protected HashMap<String,String> doInBackground(String... strings) {
int count = 0;
try {
URL url = new URL(strings[0]);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int length = connection.getContentLength();
Log.i("MyTestApp",String.valueOf(length));
File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/apk");
if (!folder.exists()) {
boolean res = folder.mkdirs();
}
String appName = "bot.apk";
File apk = new File(folder,appName);
path = apk.getPath();
FileOutputStream fos = new FileOutputStream(apk);
InputStream is = connection.getInputStream();
byte[] buffer = new byte[1024];
long totalReaded = 0l;
while ((count = is.read(buffer)) != -1) {
fos.write(buffer, 0, count);
totalReaded += count;
MainActivity.percentage = (int) (totalReaded * 100) / length;
Log.i("Value",String.valueOf( MainActivity.percentage));
}
fos.flush();
fos.close();
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
HashMap<String,String> result = new <String,String>HashMap();
result.put("status","OK");
result.put("path",path);
return result;
}
@Override
protected void onPostExecute(HashMap<String,String> s) {
super.onPostExecute(s);
}
}
Flutter side:
class CheckForUpdatesState extends State<CheckForUpdates> {
static const streamChannel =
EventChannel('bot/native_services/stream');
Stream<int> percentage = Stream.empty();
checkUpdates() async {
String url = 'App link';
Map data = await NativeService.downloadFromUrl(url);
}
Stream<int> getPercentage() {
percentage = streamChannel.receiveBroadcastStream().map<int>((event) {
print(event);
return event;
});
return percentage;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Checking for updates'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Looking for latest version'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('Version 1.0.0')],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: checkUpdates,
child: Text(
'Check for updates',
style: TextStyle(color: Colors.green),
))
],
),
StreamBuilder(
initialData: percentage,
stream: getPercentage(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print('snap shot data is: ${snapshot.data}');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('percentage:${snapshot.data}')],
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('percentage:${snapshot.error}')],
);
}
},
)
],
),
);
}
}