Android uses its own encapsulated Http, Thread, Handler for asynchronous tasks

Posted by trystan on Sat, 14 Sep 2019 07:28:00 +0200

Original Link: https://my.oschina.net/zipu888/blog/549739

The directory structure is as follows:

Encapsulation of the Http protocol:

There are two main domains using the HTTP protocol: request and response. Below is the structure diagram of the Http protocol encapsulation

(1) HttpRequestInter.java: As a request domain object, the address of the client request and the httpRequest object should be available so that information such as the parameters of the client request can be obtained; in addition, public HttpResponseInter request() throws Exception; this method is used after the request request request has been executed, returns a response object (represented here by an interface)

/** * Requested Interface* @author xuliugen */
public interface HttpRequestInter {

    //Get httpRequest
    public HttpUriRequest getHttpRequest();

    //Get the url address of the http request
    public String getRequestURL();

    //Request server: To return a response object
    public HttpResponseInter request() throws Exception;
}

(2) As the corresponding response object in (1), HttpResponseInter.java should have the following methods: get the returned status code, get the returned stream, get the returned string data, and so on. The next method is to get the corresponding data.

/** * Responsive Interface* @author xuliugen */
public interface HttpResponseInter {

    //Return status code
    public int statusCode();

    // Return number of streams to client
    public InputStream getResponseStream() throws IllegalStateException,IOException;

    //Return byte array to client
    public byte[] getResponseStreamAsByte() throws IOException;

    //Return JSON data to client
    public String getResponseStreamAsString() throws ParseException, IOException;
}

(3) This is the implementation class of the HttpRequestImpl.java interface. We can see that we have implemented not only the HttpRequestInter interface but also the ResponseHandler. The second is the data that needs to be returned when the request request is executed and stored in a response's Handler.

public class HttpRequestImpl implements HttpRequestInter,ResponseHandler<HttpResponseInter> {

    protected HttpUriRequest httpUriRequest;// url address to get request
    private AbstractHttpClient abstractHttpClient; // client object

    // Constructive Offense
    public HttpRequestImpl(AbstractHttpClient httpClient) {
        this.abstractHttpClient = httpClient;
    }

    // get method
    public HttpUriRequest getHttpRequest() {
        return httpUriRequest;
    }

    //url to get request
    public String getRequestURL() {
        return httpUriRequest.getURI().toString();
    }

    //Execute the request request request and return? response object interfaces
    public HttpResponseInter request() throws Exception {
        return abstractHttpClient.execute(httpUriRequest, this);//Incoming ResponseHandler object
    }

    /** * Method implemented by inheriting ResponseHandler interface * Processing interface for response object after execution */
    public HttpResponseInter handleResponse(HttpResponse response)throws ClientProtocolException, IOException {
        //Returns the class that implements HttpResponseInter: returns to a response interface
        HttpResponseInter httpResponseInter = new HttpResponseImpl(response); //response is required when returning
        return httpResponseInter;
    }
}

(4) Then there is the implementation class for the interface: HttpResponseImpl.java can see an HttpResponse response object in the construction method, which is the response object returned by the handler after the request has been executed.

/** * Implementation class of interface* @author xuliugen */
public class HttpResponseImpl implements HttpResponseInter {

    private HttpResponse response; // HttpResponse object
    private HttpEntity entity; // HttpEntity Test Object

    public HttpResponseImpl(HttpResponse response) throws IOException {

        this.response = response;
        HttpEntity tempEntity = response.getEntity();// Get the entity returned by the server
        if (null != tempEntity) {
            entity = new BufferedHttpEntity(tempEntity);
        }
    }

    // Returns the status code of the response object
    public int statusCode() {
        return response.getStatusLine().getStatusCode();
    }

    // stream to get results
    public InputStream getResponseStream() throws IllegalStateException,
            IOException {

        InputStream inputStream = entity.getContent();
        return inputStream;
    }

    // The result is converted to string
    public String getResponseStreamAsString() throws ParseException,
            IOException {
        return EntityUtils.toString(entity);
    }

    // The result is converted to an array of characters
    public byte[] getResponseStreamAsByte() throws IOException {
        return EntityUtils.toByteArray(entity);
    }
}

(5) ExecuteHttpPost.java is a class that inherits two main construction methods from HttpRequestImpl.java. The construction method is the actual post request method and parameter settings:

/** * Is this the real place to execute post requests? * * Inherit HttpRequestImpl to implement client-to-server requests? * * @author xuliugen * */
public class ExecuteHttpPost extends HttpRequestImpl {

    public ExecuteHttpPost(AbstractHttpClient httpClient, String url) {
        this(httpClient, url, null);
    }

    public ExecuteHttpPost(AbstractHttpClient httpClient, String url,HttpEntity entity) {
        super(httpClient);//httpClient in parent class

        this.httpUriRequest = new org.apache.http.client.methods.HttpPost(url);// Initialize httpUriRequest

        if (null != entity) {// Setting parameters
            ((HttpEntityEnclosingRequestBase) httpUriRequest).setEntity(entity);
        }
    }
}

(6) Another important class is the implementation of the client: BaseHttpClient.java here we set up a series of methods to implement the request methods of different clients, and how to convert the parameters requested by the client into the parameter types of post requests, and the returned data into the corresponding formats.The cascade calls of the method, I hope you can calm down and look slowly.

/** * HttpClient Top Class of Client */
public class BaseHttpClient {

    private AbstractHttpClient httpClient;

    public static final int DEFAULT_RETIES_COUNT = 5;

    protected int retriesCount = DEFAULT_RETIES_COUNT;

    // Set maximum number of connections
    public final static int MAX_TOTAL_CONNECTIONS = 100;

    // Set the maximum wait time to get a connection
    public final static int WAIT_TIMEOUT = 30000;

    // Set the maximum number of connections per route
    public final static int MAX_ROUTE_CONNECTIONS = 100;

    // Set connection timeout
    public final static int CONNECT_TIMEOUT = 10000;

    // Set Read Timeout
    public final static int READ_TIMEOUT = 10000;

    /** * Construct method, call initialization method */
    public BaseHttpClient() {
        initHttpClient();
    }

    /** * Initialize client parameters */
    private void initHttpClient() {

        //http parameters
        HttpParams httpParams = new BasicHttpParams();

        //Set maximum number of connections
        ConnManagerParams.setMaxTotalConnections(httpParams,MAX_TOTAL_CONNECTIONS);

        //Set the maximum wait time to get a connection
        ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);

        //Set the maximum number of connections per route
        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);

        // Set connection timeout
        HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);

        // Set Read Timeout
        HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);

        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));//Set Port 80
        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));//Set Port 443

        //Is the one that manages SchemeRegistry
        ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);

        httpClient = new DefaultHttpClient(clientConnectionManager, httpParams);

        //Create a handler for http reconnection
        httpClient.setHttpRequestRetryHandler(new BaseHttpRequestRetryHandler(retriesCount));
    }

    /** * Convert parameters to a collection of List <BasicNameValuePair> */
    private List<BasicNameValuePair> parseParams(HashMap<String, Object> params) {

        if (params == null || 0 == params.size()){
            return null;
        }

        List<BasicNameValuePair> paramsList = new ArrayList<BasicNameValuePair>(params.size());

        for (Entry<String, Object> entry : params.entrySet()) {
            paramsList.add(new BasicNameValuePair(entry.getKey(), entry.getValue() + ""));
        }
        return paramsList;
    }

    /** * Request to the server: when only URLs have no parameters */
    public String post(String url) throws Exception {
        return post(url, null); //post executed when a parameter is called and set to null
    }

    /** * post Return T-type results after request */
    public <T> T post(String url, HashMap<String, Object> params, Class<T> clz) throws Exception {
        String json = post(url, params);
        return JSONUtil.fromJson(json, clz); //Convert to Specific Type Return
    }

    /** * Other functions call this method indirectly when the request has parameters */
    public String post(String url, HashMap<String, Object> params) throws Exception {

        //Convert the incoming parameter to a parameter entity: Convert params to an enrity object: Formentity
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));

        return request(url, entity).getResponseStreamAsString();

    }

    /** * Return results of post execution directly */
    public Result postAsResult(String url, HashMap<String, Object> params)throws Exception {
        return post(url, params, Result.class);
    }

    /** * Return the result of post execution as a Stream */
    public InputStream postAsStream(String url, HashMap<String, Object> params) throws Exception {

        //Converts an incoming parameter to a parameter entity
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));

        return request(url, entity).getResponseStream();
    }

    public HttpResponseInter request(String url, HttpEntity entity) throws Exception {

        HttpRequestImpl httpRequestImpl = new ExecuteHttpPost(httpClient, url, entity);

        return httpRequestImpl.request();

    }
}

(7) The last is the number of times we use a BaseHttpRequestRetryHandler.java in httpClient to implement repeated requests on the network

/** * http Retry Connection: Primarily used to complete an attempt to reconnect* @author xuliugen */ public class BaseHttpRequestRetryHandler implements HttpRequestRetryHandler { private int max_retry_count;// Maximum number of attempts to connect public BaseHttpRequestRetryHandler(int maxretryCount) { this.max_retry_count = maxretryCount; } private static HashSet<Class<? extends IOException>> exceptionWhiteList = new HashSet<Class<? extends IOException>>(); private static HashSet<Class<? extends IOException>> exceptionBlackList = new HashSet<Class<? extends IOException>>(); static { exceptionWhiteList.add(NoHttpResponseException.class); exceptionWhiteList.add(UnknownHostException.class); exceptionWhiteList.add(SocketException.class); exceptionBlackList.add(SSLException.class); exceptionBlackList.add(InterruptedIOException.class); exceptionBlackList.add(SocketTimeoutException.class); } public boolean retryRequest(IOException exception, int executionCount,HttpContext context) { if (executionCount > max_retry_count){ return false; } if (exceptionBlackList.contains(exception.getClass())){ return false; } if (exceptionWhiteList.contains(exception.getClass())){ return true; } HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST); boolean idempotent = (request instanceof HttpEntityEnclosingRequest); if (!idempotent) { // Wulu Xuan baked a ton of Xuan return true; } Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT); boolean sent = (b != null && b.booleanValue()); if (!sent) { return true; } return false; } } 

Combination of Service and syncTask

The general process is as follows:

(1) We unified the tasks for Service, so we needed a Task entity

public class Task {

    private int taskId;// Task ID
    private Map<String, Object> taskParams;// parameter

    public static final int USER_LOGIN = 1; //A custom task ID
    //Construction method and get, set method omit
}

(2) Below is Service which manages Task in a unified way. In Service, we need not only to manage Task as an asynchronous task, but also to manage the operation of updating the interface. Because the operation of updating the interface can no longer reside in the UI, we need a unified management activity. In Service, we perform different tasks.The operation of the step task has been implemented using Thread and Handler.

public class MainService extends Service implements Runnable {

    // Task Queue: The queue used to store tasks
    private static Queue<Task> tasks = new LinkedList<Task>();

    // Add UI that needs to be updated to the collection
    private static ArrayList<Activity> appActivities = new ArrayList<Activity>();

    private boolean isRun;// Whether to run threads

    Handler handler = new Handler() {

        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case Task.USER_LOGIN: {// User Login: Update UI
                //Find activity by name: Since MAinActivity implements the MainActivityInter interface, it can be strongly converted to MainActivityInter type
                MainActivityInter activity = (MainActivityInter) getActivityByName("MainActivity"); 
                activity.refresh(msg.obj.toString());
                break;
            }
            default:
                break;
            }
        };
    };

    /** * Add Task to Task Queue */
    public static void newTask(Task t) {
        tasks.add(t);
    }

    @Override
    public void onCreate() {

        isRun = true;
        Thread thread = new Thread(this);
        thread.start();

        super.onCreate();
    }

    /** * Keep the service iterating through execution */
    public void run() {

        while (isRun) { // To listen for tasks
            Task task = null;
            if (!tasks.isEmpty()) { // Determine if there are values in the queue
                task = tasks.poll();// Remove the change from the task queue after executing the task
                if (null != task) {
                    doTask(task); // TO DO: Perform tasks
                }
            }
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
    }

    // Processing Tasks
    private void doTask(Task task) {
        Message msg = handler.obtainMessage();
        msg.what = task.getTaskId();

        switch (task.getTaskId()) {
        case Task.USER_LOGIN: { // User Login
            HashMap<String, Object> paramsHashMap =  (HashMap<String, Object>) task.getTaskParams();

            //Access the network to determine if a user exists
            String url = "http://172.23.252.89:8080/igouServ/userlogin.action";
            BaseHttpClient httpClient = new  BaseHttpClient();
            try {
                String result = httpClient.post(url, paramsHashMap);
                msg.obj= result; //Return to handler for processing
            } catch (Exception e) {
                e.printStackTrace();
            }

            break;
        }

        default:
            break;
        }

        handler.sendMessage(msg);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /** * Add an Activity object to the collection */
    public static void addActivity(Activity activity) {

        if (!appActivities.isEmpty()) {

            for (Activity ac : appActivities) {
                if (ac.getClass().getName().equals(ac.getClass().getName())) {
                    appActivities.remove(ac);
                    break;
                }
            }
        }
        appActivities.add(activity);
    }

    /** * Get Activity Objects from Activity's Name */
    private Activity getActivityByName(String name) {

        if (!appActivities.isEmpty()) {
            for (Activity activity : appActivities) {
                if (null != activity) {
                    if (activity.getClass().getName().indexOf(name) > 0) {
                        return activity;
                    }
                }
            }
        }
        return null;
    }

    /** * Exit System */
    public static void appExit(Context context) {
        // Finish All Activities
        for (Activity activity : appActivities) {
            if (!activity.isFinishing())
                activity.finish();
        }

        // End Service
        Intent service = new Intent("com.xuliugen.frame.task.MainService");
        context.stopService(service);
    }
}

(3) There are two ways to write an Interface interface MainActivityInter.java in order to enable Service to manage the activity uniformly. One way is to refresh the interface so that we can operate the interface in the service.

public interface MainActivityInter {

    /** * Initialization operation */
    public void init();

    /** * Refresh UI */
    public void refresh(Object... params);
}

testing procedure

(1) MainActivity.java was created mainly to simulate a login operation, where we need to turn on the service. Unfortunately, UN should like to get a task, add it to the task queue managed by Service, and then leave the other operations to MainService.java (Service) for operation.

public class MainActivity extends Activity implements MainActivityInter {

    private Button btn_login;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_login = (Button) this.findViewById(R.id.btn_login);
        textView=  (TextView) this.findViewById(R.id.textView1);

        // Start Services
        Intent serviceIntent = new Intent(this, MainService.class);
        startService(serviceIntent);

        btn_login.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {

                //Construction parameters are passed to Task for processing
                Map<String, Object> paramsHashMap = new HashMap<String, Object>(2);
                paramsHashMap.put("userName", "xuliugen");
                paramsHashMap.put("password", "123456");

                Task task = new Task(Task.USER_LOGIN, paramsHashMap);
                MainService.newTask(task);
            }
        });

        // Put activity into the activity queue collection
        MainService.addActivity(this);
    }

    /******************** The following two methods are *******************in the MainActivityInter interface*/
    public void init() {

    }

    public void refresh(Object... params) {
        //Update UI based on returned parameters 
        textView.setText(params[0].toString());
    }

}


Project download address: https://github.com/xuliugen/HttpAndAsyncTaskFrame

Reprinted at: https://my.oschina.net/zipu888/blog/549739

Topics: Java JSON network Apache