一个界面如何用于不同的后台android任务?

Posted

技术标签:

【中文标题】一个界面如何用于不同的后台android任务?【英文标题】:How one interface can be used for different background android tasks? 【发布时间】:2012-12-24 13:47:35 【问题描述】:

好吧,我有一个带有两个后台任务(Async-Task)的活动类,它们已在两个单独的类中定义,例如

public class GettingBeaconsList extends AsyncTask<String, String, String> 
public class GettingAirports extends AsyncTask<String, String, String> 

MainClass中初始化并执行

public class MainClass extends Activity implements DelegateTaskCompleted


     int ServiceBoolean = 0;
     public OnClickListener LoadingBeaconList = new OnClickListener()
     
         public void onClick(View v)
         
             ServiceBoolean  =1;
             new GettingBeaconsList (context,MainClass.this).execute();
         
     

    public OnClickListener LoadingAirportList= new OnClickListener()
    
         public void onClick(View v)
         
             ServiceBoolean  =2;
             new GettingAirports(context,MainClass.this).execute();
         
    


    @Override
    public void JsonArrayLoaded(JSONArray result) 
    
        // bla bla or whatever here i write, does not matter
         if(ServiceBoolean  == 1)    
                //  Load activity 5
         

         else if( ServiceBoolean  == 2)
          
             //  Load activity 6

         

        else if( ServiceBoolean==3 )
        
            // display Toast or Alert Box or load Activity number 8
         


    


现在在上面的代码中 MainClass.this 像这样存储为 AsynTask 子类中的接口引用

private Context context             =   null;
private DelegateTaskCompleted delegate      =   null;

public GettingBeaconsList (Context context,DelegateTaskCompleted delegate)  
   
    this.context        =   context;
    this.delegate       =   delegate;


// And constructor of second AsynTask is same as of First AsynTask Class

private Context context             =   null;
private DelegateTaskCompleted delegate      =   null;

public GettingAirports (Context context,DelegateTaskCompleted delegate) 
   
    this.context        =   context;
    this.delegate       =   delegate;

每个 AsynTask 类或子类的 onPostExecute,JSONArray 返回或传回调用类,如下所示。在这种情况下,调用类是 MainClass,但还有其他活动类使用相同的 AsynTask 类(GettingBeaconsListGettingAirports

protected void onPostExecute(String file_url)   
           
    pDialog.dismiss();      
    delegate.JsonArrayLoaded(gotNearestbeacons);

现在我在 MainClass 中有一个方法 (JsonArrayLoaded) 来处理来自两个不同后台任务或服务的两个响应。我正在使用条件来确定执行了哪个服务/类或 AsynTask。

但我要求最好的方法来解决这种情况,比如我们将来有 5 个或更多后台服务,它们也只是返回一个 JSON 数组,所以我需要为每个服务创建单独的接口吗?

在这种情况下,面向对象的方法应该是什么?

【问题讨论】:

接口名称也不合适。请在发布示例时遵循正确的命名约定。 结果在 Main 类中使用在哪里?请编辑帖子并给出一个适当的例子。没有意义 @MuhammadIrfan 那么,对于您关于面向对象的方式来解决您的问题的问题,您选择了最不面向对象的答案?我会说这很酷。 :) 【参考方案1】:

这是回调接口

public interface CallbackReceiver 
    public void receiveData(Object result);


使用 Asynctask 类作为抽象类

public abstract class JsonDataCallback extends AsyncTask<String, String, String> implements CallbackReceiver 
private ProgressDialog mProgressDialog;
Handler handler;
Runnable callback;
Activity activity;


public JsonDataCallback(Activity activity) 

     this.activity=activity;
     mProgressDialog = new ProgressDialog(activity);
     mProgressDialog.setMessage("Loading Please Wait.");
     mProgressDialog.setIndeterminate(false);
     mProgressDialog.setMax(100);
     mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
     mProgressDialog.setCancelable(true);


public abstract void receiveData(Object object);
@Override
protected void onPreExecute() 
    mProgressDialog =ProgressDialog.show(activity, "", "Please Wait",true,false);
    super.onPreExecute();


@Override
protected String doInBackground(String... aurl) 
    String results="";
    // do stuff
    return results;



@Override
protected void onPostExecute(String jsonData) 
     if (mProgressDialog != null || mProgressDialog.isShowing())
         mProgressDialog.dismiss();
 
     if(jsonData!=null)
     
         receiveData(jsonData);
     


在你的代码中像这样使用它

String url = ipaddress + "/GrantAdvanceList;
            JsonDataCallback callbackservice = new JsonDataCallback(yourActivity.this) 
                @Override
                public void receiveData(Object object) 
                    jsonRecordsData = (String)object;
                    //do stuff with call back data
                
            ;

        callbackservice.execute(url, null, null);

您可以通过这种方式重用代码。

【讨论】:

【参考方案2】:

我能想到的最简单的解决方案是修改您的DelegateTaskCompleted 界面,使其看起来像这样:

public interface DelegateTaskCompleted
  public void JsonArrayLoaded(AsyncTask<String, String, String> task, 
                              JSONArray result);

然后你的onPostExecute 会像下面这样发回自己:

protected void onPostExecute(String file_url)   
           
    pDialog.dismiss();      
    delegate.JsonArrayLoaded(this, gotNearestbeacons);

最后,在您的MainClass 中,您可以根据AsyncTask 的类型进行条件检查:

 @Override
 public void JsonArrayLoaded(AsyncTask<String, String, String> task,
                             JSONArray result) 
    
         if(task instanceof GettingBeaconsList)  
                //  do stuff related to the beacon list
         

         else if(task instanceof GettingAirports)  
            // do airport list stuff
         

    

这样您就可以轻松识别通过响应发送的AsyncTask,而无需跟踪它是什么,如果一个比另一个花费更长的时间等等。


或者,拥有另一个扩展 AsyncTask 的类,但添加了一个用于标识的抽象变量。

public class TrackableAsyncTask extends AsyncTask<String, String, String>
    public abstract int getId();

    public static final int ID_AIRPORT = 1;
    public static final int ID_BEACONS = 2;
    ... etc

然后让你的 Airport 和 Beacon AsycTasks 扩展它。这将要求他们实现getId 方法。

public class GettingAirports extends AsyncTask<String, String, String> 
     public int getId()
        return ID_AIRPORT;
     

然后,您可以执行switch 语句,而不是执行条件if (task instanceof GettingAirports),如下所示:

switch(task.getId())
   case TrackableAsyncTask.ID_AIRPORT:
       // run airport code

希望这会有所帮助。

【讨论】:

你能描述一下你认为你的方法是面向对象的吗?引用 OP:什么应该是面向对象的出路?【参考方案3】:

你也可以考虑使用Handler来实现。 在 Activity 中创建处理程序, 将此处理程序对象传递给每个 AsyncTask。 在 onPost 调用 handler.sendEmptyMessage(CONSTANT_INT); 在 Handler 中的 handleMessage 检查 msg.what in if 或 switch 这样,只会在多个处理程序中创建和使用一个对象 从一个活动中调用异步

【讨论】:

你能描述一下你认为你的方法是面向对象的吗?引用 OP:什么应该是面向对象的出路?【参考方案4】:

如果我正确理解您的问题,您已经扩展了 AsyncTask 几次。这些子类中的每一个的目标是将 JSONArray 传递给实现了 DelegateTaskCompleted 的 Activity,您将在其中对其执行某些操作。挑战在于,您想要对这个 JSONArray 执行的“某事”会有所不同,具体取决于 AsyncTask 生成它的原因。

抛开这些假设,有很多不同的方法可以区分 JSONArray 来自哪个 AsyncTask:

    在您的 DelegateTaskCompleted 类文件中为它需要处理的每种 JSONArray 类型创建一个静态变量,例如一个 int。向JsonArrayLoaded添加一个与此变量相同类型的参数。然后,使用您的 if-else 语句或 switch 语句来检查该变量与该集合并基于它对您的 JSONArray 执行操作。 如果您有权访问生成 JSONArray 的任何内容,请使 Array 的 0 索引包含有关如何解析它的信息。 (也许您可以将其与 if-else 或 switch 进行比较) 按照您的建议,为每个 AsyncTask 创建一个单独的接口,或者为一个具有多个方法的单个接口。

只有选项 3 是面向对象的解决方案,正如 cmets 中所指出的,由于可扩展性,这不是一个很好的解决方案。这绝不是一个完整的列表,只是我的一些想法。

不过,我想知道,是否值得使用接口,或者至少以呈现的方式。既然您已经有不同的 AsyncTask 子类来处理生成 JSONArrays,为什么不在主线程外的 doInBackground() 方法中对它们执行任何操作,或者至少将该逻辑放在这些类中的某个位置,并且只返回结果(或者可能将它们填充到数据库中并在需要时获取它们)。我认为可能有更好的方法来处理您的 AsyncTask 继承,它可以避免所有接口,或者至少以不同的方式使用它们,例如所有 AsyncTask 都实现一个接口。

如果您能澄清您的问题并解释 AsyncTasks 和您的 JSONArrayLoaded 方法正在执行的操作类型,以及您以您的方式使用接口的原因,那将非常有帮助。对于您的代码实际在做什么和希望实现什么的信息很少,很难就 OO 最佳实践给出具体的 OO 答案或建议。

【讨论】:

我在 doInBackground() 中以 json 格式从数据库中获取记录。不同的 AysnTask 以 JSONArray 的形式获取不同的记录。 之所以使用接口,是因为还有其他活动类使用相同的 AsynTask,因此要使 AsynTask 通用。 onPostExecute 方法,我使用 delegate.JsonArrayLoaded(gotNearestbeacons); delegate 是一个代表这里所有活动类的接口。 JSONArrayLoaded 执行不同的操作。我在这部分修改了一个问题 在我看来,只有您的第三个建议解决了 OP 对 面向对象 解决方案 () 的要求——仍然,它不能扩展 以面向对象的方式。 @Class Stacker - 你是绝对正确的,当时很难找出 OP 实际想要做什么,所以我真的希望能抛出一些初步的想法并得到有关该问题的更多信息。我会更新我的答案以反映您的观点,谢谢!【参考方案5】:

向两个 AsyncTask 类添加一个成员变量,即

public class GettingBeaconsList extends AsyncTask<String, String, String> 
public class GettingAirports extends AsyncTask<String, String, String> 

作为

private int flag;

并为此编写setter

public void setFlag(int flag) 

  this.flag = flag;

在主类中:

GettingBeaconsList beaconsList = new GettingBeaconsList(context,MainClass.this);
beaconsList.setFlag(100); //Declare this flag in integer constant.
beaconsList.execute();

GettingAirports airports = new GettingAirports(context, MainClass.this);
airports.setFlag(200);
airports.execute();

在两个 AsyncTask 类中都使用委托的方法传递标志:-

protected void onPostExecute(String file_url)   
           
  pDialog.dismiss();      
  delegate.JsonArrayLoaded(gotNearestbeacons, flag);
  //Change the signature of this method

再次在 MainClass 中处理响应:-

@Override
public void JsonArrayLoaded(JSONArray result, int flag) 


    // Here check which Background task has been completed
    if(flag == 100) // GettingBeaconsList is executed

    if(flag == 200) // GettingAirport is executed.


    // bla bla or whatever here i write, does not matter
     if(ServiceBoolean  == 1)    
            //  Load activity 5
     

     else if( ServiceBoolean  == 2)
      
         //  Load activity 6

     

    else if( ServiceBoolean==3 )
    
        // display Toast or Alert Box or load Activity number 8
     



【讨论】:

如果我以正确的方式理解您的问题,那么这将对您有所帮助。祝你好运。 你能描述一下你认为你的方法是面向对象的吗?引用 OP:什么应该是面向对象的出路?【参考方案6】:

我建议您对实现DelegateTaskCompleted 的Activity 使用n 内部类。它简单、高效、清晰且易于理解(想想维护)——当您构建 AsyncTasks 时,您已经传递了委托,那么您是否已经想到了这种方法?

public class MainClass extends Activity 
    // probabnly not static ;)
    private final class BeaconsDelegate implements DelegateTaskCompleted ...
    private final class AirportsDelegate implements DelegateTaskCompleted ...

【讨论】:

以上是关于一个界面如何用于不同的后台android任务?的主要内容,如果未能解决你的问题,请参考以下文章

Android N 四大组件的工作原理

如何完成在后台播放录音的任务并从事不同的活动?

如何清晰地掌握 Android 应用中后台任务的执行情况?

用于在后台运行日常任务的 Android WorkManager api

Android 后台任务执行

在Android中用Kotlin的Anko运行后台任务(KAD 09)