Android socket communcation - android

Im building an android application that connects to a server trough a socket. However I can't notify the mainthread from the worker connection thread, since it would lock the mainthread and android does not allow that. Here is the following code I have:
Part of connection controller:
public void run(){
while (true){
while (isRunning){
if (serverSocket == null){
try{
serverSocket = new Socket("*", *);
out = new PrintWriter(serverSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
serverSocket.getInputStream()));
}
catch (IOException e){
e.printStackTrace();
}
}
if (message != ""){
try{
System.out.println(message);
out.println(message);
message = "";
reply = in.readLine();
isRunning = false;
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public void sendMessage(String path,
HashMap<String, HashMap<String, String>> variables){
this.reply = "";
isRunning = true;
String variablesJSON = JSONValue.toJSONString(variables);
this.message = path + ":" + variablesJSON;
}
Authentication class:
public boolean register(String email, String password, String displayName) {
String path = "User.register";
HashMap<String, String> variables = new HashMap<String, String>();
HashMap<String, HashMap<String, String>> user = new HashMap<String, HashMap<String, String>>();
String hashedPass;
try{
hashedPass = sha1(password);
}
catch (NoSuchAlgorithmException e){
e.printStackTrace();
return false;
}
variables.put("displayname", displayName);
variables.put("password", hashedPass);
variables.put("email", email);
user.put("User", variables);
connection.sendMessage(path, user, this);
final ProgressDialog progDailog = ProgressDialog.show(context,
"Please wait..", "Register", true);
new Thread(){
public void run(){
try{
while (ConnectionController.getInstance().getIsRunning()){
sleep(200);
}
progDailog.dismiss();
}
catch (Exception e){
e.printStackTrace();
}
}
}.start();
Toast.makeText(context.getApplicationContext(),
ConnectionController.getInstance().getReply(), Toast.LENGTH_LONG)
.show();
if (ConnectionController.getInstance().getReply() != "SUCCESS"){
return false;
}
return true;
}
I need to wait for the ProgressDialog to finish, however I can't find a way. I don't think an AsyncTask is the right way todo this since the connection needs to be open all the time. Any hints or ideas?

AsyncTask is the right way to handle this. And all that you need to do to the UI, do it in your onPost method. Infact if you want to call the async in a activity and update the views from the same activity you can
Use a Broadcast receiver
Make AsyncTask class a inner class to your activity. That way the class methods can modify the views of that activity.
I use trick 2, to get things working. Once I have them working, I take the AsyncTask out, and use BroadcastReceiver's. Just to ease my development and demo time :)

Related

android make an object from extends AsyncTask and send it to another activity

I have problem with create an object from sshconnection that extends AsyncTask class..
My class is as below
public class sshconnection extends AsyncTask<String, Void, String> {
ByteArrayOutputStream baos;
protected String doInBackground(String... params) {
try {
String results;
JSch jsch = new JSch();
Session session = jsch.getSession(User, IP, Port);
session.setPassword(Pass);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
ChannelExec channel = (ChannelExec)session.openChannel("exec");
InputStream in = channel.getInputStream();
baos = new ByteArrayOutputStream();
channel.setOutputStream(baos);
channel.setCommand(CMD);
channel.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
System.out.print(new String(tmp, 0, i));
}
if(channel.isClosed()){
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
return baos.toString();
} catch (Exception e) {
//Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
//System.out.println(e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(String result) {
// TextView txt = (TextView) findViewById(R.id.txtipinfo);
// txt.setText("Executed");
Toast.makeText(MainActivity.this, baos.toString(), Toast.LENGTH_LONG).show();
}
}
and this is when i called it in button click
btn = (Button) findViewById(R.id.button_conn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
CMD = "ls";
new sshconnection().execute(User,Pass,IP,CMD);
}
});
Until here my code is work fine and I can make ssh connection to my server.
My problem is that I want to create an object from sshconnection class to send it to another activity to reuse the connection of the ssh to sends other ssh commands to my server.
If there is another way to do that even if i have to change AsyncTask class .. Or use anthing else like new threads.
Please tell me how to do that and where i make changes in my code.
Thanks a lot for everyone.
An AsyncTask is not designed for your use-case. Regardless of whether or not you could get it to work, you shouldn't.
From the AsyncTask documentation:
"AsyncTasks should ideally be used for short operations (a few
seconds at the most.)"
An AsyncTask is designed to run once for one operation and then return info to the UI. You should probably designate your SSH connection to run in a service and use Thread and ThreadPool methods to handle this.
I found the solution
public class sshconnection extends AsyncTask<String, Void, String> {
public String returncmd=null;
ByteArrayOutputStream baos;
protected String doInBackground(String... params) {
try {
//Sndlist=args[0];
//result=-11;
//String results;
JSch jsch = new JSch();
Session session = jsch.getSession("admin", "10.0.0.1",22);
session.setPassword("a");
//Toast.makeText(null," user = " + ss, Toast.LENGTH_SHORT).show();
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
ChannelExec channel = (ChannelExec)session.openChannel("exec");
InputStream in = channel.getInputStream();
baos = new ByteArrayOutputStream();
channel.setOutputStream(baos);
channel.setCommand("/ip hotspot user add name=nnnnnn profile=120");
channel.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
System.out.print(new String(tmp, 0, i));
}
if(channel.isClosed()){
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
returncmd = baos.toString();
return returncmd;
} catch (Exception e) {
//Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
//System.out.println(e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(String result) {
// TextView txt = (TextView) findViewById(R.id.txtipinfo);
// txt.setText("Executed");
//mProgressDialog.dismiss();
//Toast.makeText(UserDetail.this, baos.toString(), Toast.LENGTH_LONG).show();
}
} // end of class sshconnection
and you can call as follow:
new sshconnection().execute;

How to cancel the async task with tab host

I am currently working with the tab host and fragment. Currently I set fragment a to download json A and fragment B to download json B , the problem is when I switch fragment, the fragment A onPostExecute function will fall into the fragment B one , is there any way to fix this?
Thanks
Tab host:
tabHost = (FragmentTabHost) findViewById(R.id.tabhost);
tabHost.setup(this, getSupportFragmentManager(), R.id.tabcontent);
tabHost.addTab(
tabHost.newTabSpec("Home").setIndicator("",
res.getDrawable(R.drawable.btn_about)), Home.class,
null);
tabHost.addTab(
tabHost.newTabSpec("About").setIndicator("",
res.getDrawable(R.drawable.btn_about)), About.class,
null);
The async task
public class JSONReader {
public static final String TAG = "JSONReader";
public ArrayList<Record> records;
public Record myRecordObj;
public ArrayList<GalleryImage> images;
public String url;
public int failCount = 0; // retry twice
public Context ctx;
public String readCase;
public JSONReader(String _url, Context _ctx , String _readCase) {
url = _url;
ctx = _ctx;
readCase = _readCase;
}
public void getJSON() {
new JSONDownload().execute(url);
}
private class JSONDownload extends AsyncTask<String, Void, JSONObject> {
// TODO Auto-generated method stub
StringBuilder builder = new StringBuilder();
String temp = "";
String json = ""; // json content
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
#Override
protected JSONObject doInBackground(String... params) {
// TODO Auto-generated method stub
try {
Log.d(TAG, "Start reading: " + url);
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
return null;
// return "Server returned HTTP " + connection.getResponseCode()
// + " " + connection.getResponseMessage();
input = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(input));
while ((temp = reader.readLine()) != null) {
builder.append(temp);
}
json = builder.toString();
} catch (Exception e) {
return null;
} finally {
try {
if (input != null)
input.close();
if (output != null)
output.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
try {
return new JSONObject(json);
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(JSONObject result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
onJSONDownloaded(result);
}
}
public void onJSONDownloaded(JSONObject result) {
// TODO Auto-generated method stub
if (result != null) {
failCount = 0;
if (readCase.equals("leaderBoard")){
records = new ArrayList<Record>();
try {
JSONObject myRecord = result.getJSONObject("myRecord");
if (myRecord != null) {
myRecordObj = new Record(myRecord.getString("pic"),myRecord.getString("name"),myRecord.getString("score"));
}
JSONArray topRecords = result.getJSONArray("topRecord");
for (int i = 0; i < topRecords.length(); i++) {
JSONObject topRecord = topRecords.getJSONObject(i);
String topName = topRecord.getString("name");
String topPic = topRecord.getString("pic");
String topScore = topRecord.getString("score");
records.add(new Record(topPic, topName, topScore));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
((LeaderBoardDetail)ctx).setData(records,myRecordObj);
} else if (readCase.equals("galleryList")){
images = new ArrayList<GalleryImage>();
try {
JSONArray imageList = result.getJSONArray("images");
for (int i = 0; i < imageList.length(); i++) {
JSONObject image = imageList.getJSONObject(i);
images.add(new GalleryImage(image.getString("url"),image.getString("thumbUrl"),image.getString("category"),image.getString("userPic"),image.getString("name")));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//exception
if (((FragmentActivity) ctx).getSupportFragmentManager().findFragmentById(R.id.tabcontent).getTag().equals("Gallery")) {
PhotoGallery galleryFragment = (PhotoGallery) ((FragmentActivity) ctx).getSupportFragmentManager().findFragmentById(R.id.tabcontent);
galleryFragment.setData(images);
}
}
} else {
if (failCount <= 1) { // check global_conf twice if fail
failCount++;
Log.d(TAG, "No of retry" + failCount);
new JSONDownload().execute(url); // Retry download json
} else {
failCount = 0;
}
}
}
}
I used it in my app this way which works for me and answer is a bit similar to another answer but few additions and more detail. Hope it help you too.
NOTE: This is just a idea, u need to try, it may vary as per your app architect.
At your activity make the task object global [make sure set task obj null once task is finish]
JSDownload js = null;
public void getJSON() {
if(js != null && js.getStatus() == AsyncTask.Status.RUNNING)
{
js.cancel(true);
if(js.isCancelled())
{
js = new JSONDownload();
js.execute(url);
}
else
{
js = new JSONDownload();
js.execute(url);
}
}
At the Async class side....[make sure u take care null result # onpostExcute]
class JSONDownload extends AsyncTask<String, Void, JSONObject>
{
protected JSONObject doInBackground(String... params) {
// TODO Auto-generated method stub
try {
if(!this.isCancelled())
{
//make http connection ..
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
//as we are in steps in bg check iscancel .. again
//if its a loop here we call break; and return null once only..
if(this.isCancelled())
return null;
// connection status check and get buffer etc .. code here
if(this.isCancelled())
return null;
//read data
return data;
}
} catch (Exception e) {
return null;
}
}
#Override
protected void onCancelled(){
// If you write your own implementation, do not call super.onCancelled(result).
}
}
Use the global async task variable
LongOperation LongOperationOdeme = new LongOperation();
and set:
LongOperationOdeme.cancel(true);
This will stop any async task running at that moment, it's what the back button does
If you are not looking for a simplest answer but for a maybe more interesting and elegant, have a peek at this article, especially if you find functional programming interesting.
It's easier than it looks, i was almost unfamiliar to FP before this article but it covers common problems related to AsyncTask and asynchronicity in Android in general, so I got the gist and consider to use Observables instead of AsyncTask in the future projects myself. It is RxJava and it can solve your problem gracefully: "The fromFragment call transforms the given source observable in such a way that events will only be emitted to the fragment if it’s still alive and attached to its host activity."
One more citation form the article: "What if the user decides to back out of the Activity that triggered the task, and we are holding on to a stale reference. This not only creates a substantial memory leak, but is also worthless because meanwhile it has been detached from the application window. A problem that everyone is well aware of."

Showing loading for a UI freezing async task like this one

I have an Async task class which gets the name of a web method and run it and I have to wait for the result of that web method, so I used task.execute.get() method which is freezing my UI. the problem is that I want to show a loading dialog when task is executing but when I'm trying to call this method 10 times for 10 web methods, the UI freezes and after executing 10 web methods, loading dialog appears for 1 second.
What can I do to show loading without moving all of my codes into doInBackground? I want to have a class which gets web method info and returns the result. this is my class code:
public class AsyncCallWs extends AsyncTask<String, Void, String> {
private ProgressDialog dialog;
public String methodName="";
private WebService ws;
private ArrayList<ServiceParam> paramsList;
private boolean hasParams;
public AsyncCallWs(Activity activity,String methodName) {
xLog.position();
try {
this.dialog = new ProgressDialog(activity);
this.methodName = methodName;
hasParams = false;
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
public AsyncCallWs(Activity activity,String methodName,ArrayList<ServiceParam> params) {
xLog.position();
try {
this.dialog = new ProgressDialog(activity);
this.methodName = methodName;
this.paramsList = params;
hasParams = true;
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
#Override
protected void onPreExecute() {
this.dialog.setMessage(PersianReshape.reshape("Loading..."));
this.dialog.show();
}
#Override
protected String doInBackground(String... params) {
xLog.position();
String result = "No async task result!";
try {
ws = new WebService(PublicVariable.NAMESPACE, PublicVariable.URL);
if (!hasParams){
result = ws.CallMethod(methodName);
}
else{
xLog.info("THIS METHOD IS: "+ methodName);
result = ws.CallMethod(methodName,paramsList);
xLog.info("THIS RESULT IS: "+ result);
}
} catch (Exception e) {
xLog.error(e.getMessage());
}
return result;
}
#Override
protected void onPostExecute(String result) {
xLog.position();
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
xLog.info("Output of current AsyncTask is:"+ result);
}
}
And this is the way I'm calling web methods using this class:
public void doSync(String method){
xLog.position();
AsyncCallWs t;
ArrayList<ServiceParam> serviceParams = new ArrayList<ServiceParam>();
String result="";
Settings settings = new Settings(activity);
PublicVariable.pGuid = Login(settings.getValue("Username"), settings.getValue("Password"));
xLog.info("pGuid in doSync is:" + PublicVariable.pGuid);
serviceParams.add(new ServiceParam("pGuid", PublicVariable.pGuid, String.class));
if (method=="all" || method=="person"){
try {
t = new AsyncCallWs(activity,"GetPersonInfo",serviceParams);
result = t.execute().get();
xLog.info("Sync Person=>"+ result);
String fields[] = result.split(PublicVariable.FIELD_SPLITTER);
Person person = new Person(activity,fields);
person.empty();
person.insert();
settings.update("PersonId",String.valueOf(person.getId()));
PublicVariable.personId = person.getId();
xLog.info("Person inserted...");
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
}
if (method=="all" || method=="personImage"){
try {
t = new AsyncCallWs(activity,"GetPersonImage",serviceParams);
result = t.execute().get();
if (!result.equals("Nothing")){
settings.update("picture", result);
xLog.info("Picture updatted...");
}
else
xLog.error("NO PERSON IMAGE FOUND!");
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
if (method=="all" || method=="lawyers"){
try {
t = new AsyncCallWs(activity,"GetLawyers",serviceParams);
result = t.execute().get();
xLog.info("Sync Lawyer=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
String fields[];
Lawyer lawyer= new Lawyer(activity);
lawyer.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
lawyer = new Lawyer(activity, fields);
lawyer.insert();
}
xLog.info("Lawyers inserted...");
}
else
xLog.error("NO LAWYER FOUND!");
}catch (Exception e) {
xLog.error(e.getMessage());
}
}
if (method=="all" || method=="news"){
try {
t = new AsyncCallWs(activity,"GetNews",serviceParams);
result = t.execute().get();
String fields[];
Log.d("Ehsan","Sync News=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
News news = new News(activity);
news.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
news= new News(activity,fields);
news.insert();
}
xLog.info("News inserted...");
}
else
xLog.error("NO NEWS FOUND!");
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
if (method=="all" || method=="messages"){
try {
t = new AsyncCallWs(activity,"GetMessagesInbox ",serviceParams);
result = t.execute().get();
Log.d("Ehsan","Sync message Inbox=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
String fields[];
Message message = new Message(activity);
message.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
message= new Message(activity,fields);
message.insert();
}
xLog.info("Inbox messages inserted...");
}
else
xLog.error("NO MESSAGES FOUND!");
} catch (Exception e) {
xLog.error(e.getMessage());
}
try {
t = new AsyncCallWs(activity,"GetMessagesOutbox ",serviceParams);
result = t.execute().get();
Log.d("Ehsan","Sync message Outbox=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
String fields[];
Message message = new Message(activity);
message.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
message= new Message(activity,fields);
message.insert();
}
xLog.info("Outbox messages inserted...");
}
else
xLog.error("NO MESSAGES FOUND!");
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
if (method=="all" || method=="requests"){
try {
t = new AsyncCallWs(activity,"GetAllRequests",serviceParams);
result = t.execute().get();
Log.d("Ehsan","Sync share buy sell requests=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
String fields[];
Share share = new Share(activity);
share.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
share= new Share(activity,fields);
share.insert();
}
xLog.info("Shares inserted...");
}
else
xLog.error("NO MESSAGES FOUND!");
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
if (method=="all" || method=="financials"){
try {
t = new AsyncCallWs(activity,"GetFinancials",serviceParams);
result = t.execute().get();
Log.d("Ehsan","Sync Financials=>"+ result);
if (!result.equals("Nothing")){
String records[] = result.split(PublicVariable.RECORD_SPLITTER);
String fields[];
Financial financial = new Financial(activity);
financial.empty();
for(int i=0;i<records.length;i++){
fields = records[i].split(PublicVariable.FIELD_SPLITTER);
financial= new Financial(activity,fields);
financial.insert();
}
xLog.info("Financials inserted...");
}
else{
Log.e("Ehsan", "NOT FINANCIALS FOUND!");
}
} catch (Exception e) {
xLog.error(e.getMessage());
}
}
}
Here
result = t.execute().get(); //<<< calling get method
as in doc AsyncTask.get() :
Waits if necessary for the computation to complete, and then retrieves
its result.
so to avoid freezing of Main UI Thread during execution of doInBackground start AsyncTask without calling get method as|:
t.execute();
I want to have a class which gets web method info and returns the
result
For this you should implement callback with AsyncTask which report to Activity. see following examples :
android asynctask sending callbacks to ui
How to implement callback with AsyncTask

ProgressDialog doesn't show up in AsyncTask

I am creating an android app that depends on data that the app gets from the database. To get this data I have the following class (this class gets data from the database in JSON, translates it and returns it):
public class Json {
public String jsonResult;
private Activity activity;
private String url = "http://json.example.org/json.php";
private String db, query;
public Json(Activity activity) {
this.activity = activity;
}
public String accessWebService(String db, String query) {
JsonReadTask task = new JsonReadTask();
this.db = db;
this.query = query;
task.execute(new String[] { url });
try {
task.get();
} catch (InterruptedException e) {
Toast.makeText(activity.getApplicationContext(), "FATAL ERROR: The thread got interrupted",
Toast.LENGTH_LONG).show();
} catch (ExecutionException e) {
Toast.makeText(activity.getApplicationContext(), "FATAL ERROR: The thread wasn't able to execute",
Toast.LENGTH_LONG).show();
}
return jsonResult;
}
// Async Task to access the web
private class JsonReadTask extends AsyncTask<String, Void, String> {
private final ProgressDialog dialog = new ProgressDialog(activity);
protected String doInBackground(String... params) {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(params[0]);
try {
// add post data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("db", db));
nameValuePairs.add(new BasicNameValuePair("query", query));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
jsonResult = inputStreamToString(response.getEntity().getContent()).toString();
if (jsonResult.isEmpty()) {
Toast.makeText(activity.getApplicationContext(),
"Error, connection is up but didn't receive data. That's strange...", Toast.LENGTH_LONG)
.show();
this.cancel(true);
}
} catch (ClientProtocolException e) {
// Toast.makeText(activity.getApplicationContext(),
// "Error, Client Protocol Exception in JSON task",
// Toast.LENGTH_LONG).show();
Log.i("Json", "Error, Client Protocol Exception in JSON task");
this.cancel(true);
} catch (IOException e) {
// Toast.makeText(activity.getApplicationContext(),
// "Error, Please check your internet connection",
// Toast.LENGTH_LONG).show();
Log.i("Json", "Error, Please check your internet connection");
this.cancel(true);
}
return null;
}
private StringBuilder inputStreamToString(InputStream is) {
String rLine = "";
StringBuilder answer = new StringBuilder();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
try {
while ((rLine = rd.readLine()) != null) {
answer.append(rLine);
}
} catch (IOException e) {
Toast.makeText(activity.getApplicationContext(), "Error..." + e.toString(), Toast.LENGTH_LONG).show();
}
return answer;
}
}// end async task
}
I noticed that my app freezes while accessing the database. After some googling, I found out it was the .get() method in the accessWebService() method caused this. I tried to implement a progressDialog like so (I also deleted the .get() method):
private final ProgressDialog dialog = new ProgressDialog(activity);
protected void onPreExecute() {
super.onPreExecute();
this.dialog.setMessage("Loading...");
this.dialog.setCancelable(false);
this.dialog.show();
}
protected void onPostExecute(String result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
}
but the dialog didn't show up and I got NullPointerException because the app only works when there is data:
result = json.accessWebService(db, query);
(maybe an important thing to mention: I also use this method in for loops)
So now my question is: How can I change my app so that I get a ProgressDialog while accessing the database and without getting NullPointerException? I fear that I have to rearchitect my whole app and if I have to do this, how do I do this? I hope you guys understand my question and have a fix for this because I really need help. Thanks in advance.
P.S. Sorry if my English is not that good, I'm not a native speaker.
... I found out it was the .get() method in the accessWebService() method caused this. I tried to implement a progressDialog...
That is right. get() is a blocking call and simply adding a ProgressDialog won't fix it. You need to remove .get() and that will probably fix the issue of your ProgressDialog not showing.
An AsyncTask must be executed on the main Thread so make sure you are doing that.
Another problem you have is Toast.LENGTH_LONG).show(); runs on the UI and you have it in doInBackground() which cannot happen. You need to send the result to onPostExecute() and you can display your Toast there if need. This could also be done in onProgressUpdate().
This null pointer exception happens because of result value was null. put the condition before
if(result != null ) {
// CODE FOR PARSING
} else {
return;
}
You can start showing progress bar before asyncTask is started and finish showing when asyncTask is finished.
Pass handler to asyncTask and sendMessage onPostExecute method. Then handle message on UI thread and hide progress bar
For example there is handler field in UI (mainActivity). There you should handle hiding progress bar:
public Handler refreshChannelsHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EPGManager.ERROR_MESSAGE:
//do some stuff
break;
case EPGManager.SUCCESS_MESSAGE:
//do some stuff
break;
}
super.handleMessage(msg);
}
};
Then you can call asyncTask with your handler
epgManager.loadChannels(refreshChannelsHandler);
AsyncTask is inside the method so it looks like this:
public void loadChannels(Handler handler) {
AsyncTask task = new AsyncTask() {
#Override
protected Object doInBackground(Object[] params) {
try {
//do AsyncTask Job
} catch (Exception e) {
return new LoadingResult((Handler) params[0], false);
}
return new LoadingResult((Handler) params[0], false);
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
LoadingResult loadingResult = ((LoadingResult)o);
sendMessageToHandler(loadingResult.handler, loadingResult.isSuccess);
}
};
task.execute(handler);
}
Here is method:
private void sendMessageToHandler(Handler handler, boolean isSuccess) {
handler.sendEmptyMessage(isSuccess ? SUCCESS_MESSAGE : ERROR_MESSAGE);
}
And finally inner class
private class LoadingResult {
private Handler handler;
private boolean isSuccess;
public LoadingResult(Handler handler, boolean isSuccess) {
this.handler = handler;
this.isSuccess = isSuccess;
}
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
Ow, and don't forget constants
public static final int SUCCESS_MESSAGE = 1;
public static final int ERROR_MESSAGE = -1;
Hope it helps :)

Android animation only starts after AsyncTask completes

I am trying to create a native login screen, where the username and password boxes slide off the screen while the request is being processed (and slide back up if the login is unsuccessful).
In order to achieve that, I have defined my animation (DropDownAnimation) and I assign it to my LinearLayout (footer). When the user clicks the Login button, I start the animation, and then call a function (tryLogin()) which starts an AsyncTask. The AsyncTask handles all the work of creating and sending the login request, and getting the JSONObject response.
However, my problem is that the slideDown animation doesn't start until after the AsyncTask has completed. This doesn't look so bad on a successful login, but on a failed login it means that the LinearLayout never slides down - it jumps to the bottom of the screen, to begin the slideUp animation back to its original position.
This seems like a similar problem to this question, but I'm not doing using bindService() and all my non-UI code seems (to me) to be contained in the AsyncTask already. LogCat tells me:
06-24 04:37:35.141: I/Choreographer(5347): Skipped 137 frames! The application may be doing too much work on its main thread.
I assume those are the frames where the footer would be sliding down - but I can't figure out where it is that I'm executing things on the main thread. Here's my code for LoginPage and LoginTask.
LoginPage.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_page);
login = (Button) findViewById(R.id.login);
username = (EditText) findViewById(R.id.username);
password = (EditText) findViewById(R.id.password);
footer = (LinearLayout) findViewById(R.id.footer);
// We must wait for the layout to be finalised before trying to find heights.
ViewTreeObserver vto = footer.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
initAnimations();
}
});
loading = (TextView) findViewById(R.id.loading);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String mUsername = username.getText().toString();
String mPassword = password.getText().toString();
// Neither of these two things happen until after LoginTask is done.
footer.startAnimation(slideDown);
loading.setVisibility(TextView.VISIBLE);
tryLogin(mUsername, mPassword);
}
});
}
protected void tryLogin(String mUsername, String mPassword) {
Exception e;
String loginUrl = getString(R.string.login_url);
String clientId = getString(R.string.client_id);
String clientSecret = getString(R.string.client_secret);
LoginTask loginTask = (LoginTask) new LoginTask().execute(mUsername, mPassword, loginUrl, clientId, clientSecret);
if ((e = loginTask.getException()) != null) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
} else {
JSONObject response;
try {
response = loginTask.get();
Log.d("login", response.toString());
if (!response.has("access_token")) {
loading.setVisibility(TextView.INVISIBLE);
footer.startAnimation(slideUp);
Toast.makeText(this, "Login error", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent(this, FullscreenWebView.class);
i.putExtra("accessToken", response.get("access_token").toString());
startActivity(i);
overridePendingTransition(0, 0);
}
} catch (InterruptedException e1) {
e1.printStackTrace();
Thread.currentThread().interrupt();
} catch (ExecutionException e1) {
e1.printStackTrace();
} catch (JSONException e1) {
e1.printStackTrace();
throw new RuntimeException(e);
}
}
}
LoginTask.java
class LoginTask extends AsyncTask<String, Void, JSONObject> {
private Exception exception;
#Override
protected JSONObject doInBackground(String... params) {
HttpURLConnection connection;
OutputStreamWriter request = null;
URL url = null;
JSONObject response = null;
String parameters = "grant_type=password&username="+params[0]+"&password="+params[1]+"&client_id="+params[3]+"&client_secret="+params[4];
try {
url = new URL(params[2]);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
connection.setRequestMethod("POST");
request = new OutputStreamWriter(connection.getOutputStream());
request.write(parameters);
request.flush();
request.close();
// username or password is probably wrong
Log.d("login", ""+connection.getResponseCode());
if (connection.getResponseCode() != 200) {
return new JSONObject();
}
String line = "";
InputStreamReader isr = new InputStreamReader(connection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
Log.d("login", sb.toString());
response = new JSONObject(sb.toString());
isr.close();
reader.close();
} catch (Exception e) {
this.exception = e;
}
return response;
}
}
I've also tried making LoginTask be a member class of LoginPage, and starting the animation in the onPreExecute() method, but that didn't change anything.
Any help is much appreciated!
When you use AsyncTask.get(), you are blocking the UI thread. As the animation runs on the UI thread, it appears as if it is not running (while in fact it is blocked by your long running tryLogin method).
Instead, you should move the code that relies on the result from the LoginTask to its onPostExecute method:
protected void tryLogin(String mUsername, String mPassword) {
String loginUrl = getString(R.string.login_url);
String clientId = getString(R.string.client_id);
String clientSecret = getString(R.string.client_secret);
new LoginTask().execute(mUsername, mPassword,
loginUrl, clientId, clientSecret);
}
LoginTask.java
class LoginTask extends AsyncTask<String, Void, JSONObject> {
private Exception exception;
#Override
protected JSONObject doInBackground(String... params) {
// Unchanged
}
public void onPostExecute(JSONObject response) {
if (exception != null) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
} else {
Log.d("login", response.toString());
if (!response.has("access_token")) {
loading.setVisibility(TextView.INVISIBLE);
footer.startAnimation(slideUp);
Toast.makeText(this, "Login error", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent(this, FullscreenWebView.class);
i.putExtra("accessToken", response.get("access_token").toString());
startActivity(i);
overridePendingTransition(0, 0);
}
}
}
}

Categories

Resources