Android通过okhttp访问网络

简介

okhttp是一个第三方类库,用于Android中访问网络。这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献。用于替代HttpUrlConnection和Apache HttpClient(android API23 里已移除HttpClient)。

准备工作

在Android Studio中创建一个新Android项目。

【方式一】本地导入jar包

OkHttp jar包下载:https://search.maven.org/artifact/com.squareup.okhttp3/okhttp/4.10.0/jar

Downloads里可以下载jar和sources.jar

我在这里使用大赛提供好的jar包。

  • okhttp-4.9.0.jar
  • okio-jvm-2.8.0.jar
  • kotlin-stdlib-1.4.10.jar
  • kotlin-stdlib-common-1.4.10.jar
  • annotations-13.0.jar

由于okhttp依赖于okio(支持相关流操作),还需要把okio一并导入,但是需要知道okhttp对应的okio版本,比如4.9.0对应的是2.8.0。

另外还需要添加kotlin-stdlib库。 

切换到“Project”视图,找到app/libs目录,将这些.jar逐一复制到这个目录下。

然后,逐一在这些.jar文件上右键,选择【Add As Library】

   

【方式二】Gradle添加依赖

在build.gradle中添加依赖

implementation 'com.squareup.okhttp3:okhttp:4.10.0'

【方式三】Maven构建

在pom.xml中添加

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.10.0</version>
</dependency>

禁用掉明文流量请求的检查

1.在res目录下找到或者新建xml文件夹,在xml文件夹下新建network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:android="http://schemas.android.com/apk/res/android">
    <!--禁用掉明文流量请求的检查-->
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

2.在manifests-AndroidManifest.xml中添加刚才创建的network_security_config.xml

给<application>标记添加属性:android:networkSecurityConfig="@xml/network_security_config"

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.sdbi.smartcitytest01">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SmartCityTest01"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

否则,会报如下错误

报错信息:java.net.UnknownServiceException: CLEARTEXT communication to 124.93.196.45 not permitted by network security policy

 W/System.err: java.net.UnknownServiceException: CLEARTEXT communication to 124.93.196.45 not permitted by network security policy
 W/System.err:     at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:188)
 W/System.err:     at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
 W/System.err:     at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
 W/System.err:     at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
 W/System.err:     at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
 W/System.err:     at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
 W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
 W/System.err:     at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
 W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
 W/System.err:     at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
 W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
 W/System.err:     at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
 W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
 W/System.err:     at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
 W/System.err:     at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
 W/System.err:     at com.sdbi.smartcitytest01.MainActivity$1$1.run(MainActivity.java:51)
 W/System.err:     at java.lang.Thread.run(Thread.java:764)

添加网络请求权限

在manifests-AndroidManifest.xml中添加:<uses-permission android:name="android.permission.INTERNET" />

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.sdbi.smartcitytest01">

    <!-- 允许用户访问网络,这一行要加在<manifest>的下一行 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SmartCityTest01"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

还需要在Activity启动时,动态获取权限:

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;

import com.sdbi.smartcityli01.R;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
            // 动态申请权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET}, 1);
        }
    }
}

Get方式

我们知道,请求消息本质上就是客户端告诉服务器的一些信息。请求消息由请求行、请求头、请求体组成。

Get方式没有请求体,请求参数在请求行中,在url后。

1、基本步骤

1.构建网络访问OkHttpClient对象

OkHttpClient okHttpClient = new OkHttpClient(); // 创建OkHttpClient对象

2.创建请求对象request

Request request = new Request.Builder().url("https://www.baidu.com").build(); // 创建一个Request对象,设置请求参数

3.创建Call对象

Call call = okHttpClient.newCall(request); // 通过okHttpClient构造一个Call对象,将你的请求request封装成进去

4.创建接收返回数据的对象response,并执行请求

Response response = call.execute(); // 得到响应对象

5.获取响应体

String  strBody = response.body().string();

注意!response.body().string() 只能调用一次!

因为响应主体 ResponseBody持有的资源可能会很大,所以 OkHttp 并不会将其直接保存到内存中,只是持有数据流连接。只有当我们需要时,才会从服务器获取数据并返回。同时,考虑到应用重复读取数据的可能性很小,所以将其设计为一次性流(one-shot),读取后即 “关闭并释放资源”

2、编写Get同步请求方法

注意这个方法不能在主线程中直接调用,因为主线程不允许访问网络。

public String getJsonByGetSync(String url) throws Exception {
    OkHttpClient okHttpClient = new OkHttpClient();  // 创建OkHttpClient对象
    Request request = new Request.Builder().url(url).build(); // 创建一个Request对象,设置请求参数
    Call call = okHttpClient.newCall(request);
    Response response = call.execute(); // 得到响应对象
    if (response.isSuccessful()) {
        return response.body().string();
    }
    return null;
}

3、编写Get异步请求

异步Get请求一般用来请求下载文件或者图片,不会用来获取Json返回值,所以一般不会返回字符串。

public void getJsonByGetAsync(String url) {
    OkHttpClient okHttpClient = new OkHttpClient();  // 创建OkHttpClient对象
    Request request = new Request.Builder().url(url).build(); // 创建一个Request对象,设置请求参数
    Call call = okHttpClient.newCall(request);

    call.enqueue(new Callback() { // 异步请求得到响应对象
        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {
            Log.d(TAG, "onFailure: e = " + e.getMessage());
        }

        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
            if (response.isSuccessful()) {
                Log.d(TAG, "onResponse:response = " + response.body().string());
            }
        }
    });
}

Post方式

Post请求方式与Get方式类似,但是,Post方式请求服务器,请求的参数是需要放到请求体中的,这样我们就需要使用RequestBody来封装请求体。

在创建RequestBody对象之前,我们需要先指定请求体的数据类型MediaType。

1、请求体的数据类型MediaType

(1)multipart/form-data

以表单形式提交,主要是上传文件用它。

它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。

当上传的字段是文件时,会有Content-Type来说明文件类型;content-disposition,用来说明字段的一些信息.

使用Postman测试

(2)application/x-www-from-urlencoded

会将表单内的数据转换为键值对,比如:name=java&age=23

(3)raw

选择Text,则请求头是: text/plain

选择JavaScript,则请求头是: application/javascript

选择JSON,则请求头是: application/json(如果想以json格式传参,就用raw+JSON就行了)

选择HTML,则请求头是: text/html

选择XML,则请求头是: application/xml

(4)binary

相当于Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。(一般用的不多)

注意:Postman中 Params和Body的区别

Params:它会将参数放入url的?后面提交到后台(带到请求的接口链接里)

Body:是放在请求体里面。

2、编写Post请求方法

public String getJsonByPost(String url) throws Exception {
    OkHttpClient okHttpClient = new OkHttpClient();  //创建OkHttpClient对象
    // 创建json请求体
    JSONObject json = new JSONObject();
    try {
        json.put("username", "panda");
        json.put("password", "123456");
    } catch (JSONException e) {
        e.printStackTrace();
    }

    // 创建请求体
    MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
    RequestBody requestBody = RequestBody.create(String.valueOf(json), mediaType);
    // 创建Post请求
    Request request = new Request.Builder()
            .url(url)
            .post(requestBody) // 添加post请求
            .build();
    Response response = okHttpClient.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string(); // 注意response.body().string() 只能调用一次!
    }
    return null;
}

3、POST方式上传文件

(1)调用系统打开文件窗口,获取文件路径和文件名

xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".fragment.PersonalFragment">

    <com.google.android.material.appbar.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:titleEnabled="false">

        <include layout="@layout/layout_toolbar" />
    </com.google.android.material.appbar.CollapsingToolbarLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="选择文件:"
            android:textColor="@color/blue"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btnOpen"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="打开"
            android:textColor="@color/blue"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

java文件(Android9.0 API 28可以)

import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.sdbi.smartcityli01.R;

import java.io.File;
public class PersonalFragment extends MySuperFragment {
    private static final String TAG = "PersonalFragment";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: 个人");
        View view = inflater.inflate(R.layout.fragment_personal, container, false);

        Button btnOpen = view.findViewById(R.id.btnOpen);
        btnOpen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("*/*");      //   /*/ 此处是任意类型任意后缀
                // intent.setType(“audio/*”) // 选择音频
                // intent.setType(“video/*”) // 选择视频 (mp4 3gp 是android支持的视频格式)
                // intent.setType(“video/*;image/*”)//同时选择视频和图片
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                startActivityForResult(intent, 100);
            }
        });
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setMyTitle(view, "个 人");
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 100:
                String tempPath = data.getData().getPath();
                String path = tempPath.substring(tempPath.indexOf(":") + 1);
                File sdcard = Environment.getExternalStorageDirectory();
                String realPath = sdcard.getAbsolutePath() + "/" + path;

                Log.d(TAG, "onActivityResult: realPath = " + realPath);
                File file = new File(realPath);
                if (file.exists()) {
                    Log.d(TAG, "onActivityResult: file.getName() = " + file.getName());
                } else {
                    Toast.makeText(getActivity(), realPath + "文件不存在", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

增加读取文件的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    // 申请权限
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}

 

   

选中文件我们可以通过Log看到文件的路径和名称。

(2)上传文件

使用大赛平台接口 http://10.54.38.55:10001/prod-api/common/upload

接口地址 POST  /prod-api/common/upload

接口描述 请求头需要 token 参数,具体格式参见安全认证说明

请求数据类型 multipart/form-data

请求参数

参数名称 参数说明 请求类型 必须 数据类型
file 上传的文件对象 formData false file

响应参数

参数名称 参数说明 类型
code 状态码, 200 正确, 其他错误 string
msg 返回消息内容 string
fileName 文件名 string
url 文件访问地址 string

响应示例

{
    "code": 200,
    "fileName": "test.txt",
    "url": "/profile/upload/file/test.txt",
    "msg": "操作成功"
}

修改Java代码

import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.sdbi.smartcityli01.R;

import java.io.File;
import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class PersonalFragment extends MySuperFragment {
    private static final String TAG = "PersonalFragment";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: 个人");
        View view = inflater.inflate(R.layout.fragment_personal, container, false);

        Button btnOpen = view.findViewById(R.id.btnOpen);
        btnOpen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("*/*");      //   /*/ 此处是任意类型任意后缀
                //intent.setType(“audio/*”) // 选择音频
                //intent.setType(“video/*”) // 选择视频 (mp4 3gp 是android支持的视频格式)
                //intent.setType(“video/*;image/*”)//同时选择视频和图片
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                startActivityForResult(intent, 100);
            }
        });

        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setMyTitle(view, "个 人");
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 100:
                String tempPath = data.getData().getPath();
                String path = tempPath.substring(tempPath.indexOf(":") + 1);
                File sdcard = Environment.getExternalStorageDirectory();
                String realPath = sdcard.getAbsolutePath() + "/" + path;

                Log.d(TAG, "onActivityResult: realPath = " + realPath);
                File file = new File(realPath);
                if (file.exists()) {
                    Log.d(TAG, "onActivityResult: file.getName() = " + file.getName());
                    // 上传
                    String imageType = "multipart/form-data";
                    RequestBody fileBody = RequestBody.create(file, MediaType.parse("image/jpg"));// RequestBody.create(MediaType.parse("image/jpg"), file);
                    RequestBody requestBody = new MultipartBody.Builder()
                            .setType(MultipartBody.FORM)
                            .addFormDataPart("file", file.getName(), fileBody)
                            .addFormDataPart("imagetype", imageType)
                            .build();
                    String url = "http://10.54.38.55:10001/prod-api/common/upload"; // 校内服务器
                    // 通过登录接口获取到Token
                    String token = "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI5MWI0Mjc4LTg5ZmUtNDgxNy05YjY5LTUyNmVhYThkNjI1ZiJ9.ZQcEZK2E2Q-_Ak3KzyBemw67FtbeqwhCg48yMBQPzpZVPRh1URcBjvg9NwayzP5BQvvzGmMmB9uBAM1X2FjMpA";
                    Request request = new Request.Builder()
                            .url(url)
                            .addHeader("Authorization", token) // 通过登录接口获取到Token
                            .post(requestBody)
                            .build();

                    OkHttpClient okHttpClient = new OkHttpClient();
                    okHttpClient.newCall(request).enqueue(new Callback() {
                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            String htmlStr = response.body().string();
                            Log.i(TAG, htmlStr);
                        }

                        @Override
                        public void onFailure(Call call, IOException e) {
                            Log.d(TAG, "onFailure: e = " + e.getMessage());
                        }
                    });
                } else {
                    Toast.makeText(getActivity(), realPath + "文件不存在", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

如果Token过期会提示认证失败,我们需要重写登录获取Token

上传成功返回如下信息:

{
    "msg": "操作成功",
    "fileName": "/profile/upload/2022/11/23/5cedd4cf-59fa-4e1f-b546-52a293b399f4.jpg",
    "code": 200,
    "url": "http://10.54.38.55/profile/upload/2022/11/23/5cedd4cf-59fa-4e1f-b546-52a293b399f4.jpg"
}

到服务器上查看

 

 

PUT方式

put方式和post方式类似

public String getJsonByPut(String url) throws Exception {
    OkHttpClient okHttpClient = new OkHttpClient();  //创建OkHttpClient对象
    // 创建json请求体
    JSONObject json = new JSONObject();
    try {
        json.put("id", "1471");
        json.put("name", "李华伟");
        json.put("phone", "13963856177");
        json.put("addressDetail", "烟台市芝罘区");
        json.put("label", "家庭");
    } catch (JSONException e) {
        e.printStackTrace();
    }
    // 创建请求体
    MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
    RequestBody requestBody = RequestBody.create(String.valueOf(json), mediaType);
    // 创建Put请求
    Request request = new Request.Builder()
            .url(url)
            .addHeader("Authorization", "XXXXXXXXXXXXXXXXXXXXXXX234567890")
            .put(requestBody) // 添加put请求
            .build();

    Response response = okHttpClient.newCall(request).execute();
    if (response.isSuccessful()) {
        Log.d(TAG, "getJsonByPut:response.code() = " + response.code());
        return response.body().string(); // 注意response.body().string() 只能调用一次!
    }
    return null;
}

 

 

 

 

 

 

Android不能在主线程中访问网络,不能在子线程中操作UI

Android不能在主线程中访问网络,不能在子线程中操作UI

不要阻塞主线程(UI线程)
不要从主线程以外的线程更新UI(UI线程)

1.使用runOnUiThread方法

public class MainActivity extends Activity {
    private Button btn;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv1);
        btn = (Button) findViewById(R.id.btn1);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000); // 点击按钮,等待5秒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), "MainActivity click button", Toast.LENGTH_SHORT).show();
                                tv.setText("MainActivity click button");
                            }
                        });
                    }
                }).start();
            }
        });
    }
}

2.Thread+Handler实现异步消息处理(最经典,扩展性最好,使用最广泛)

public class MainActivity extends Activity {
    private Button btn;
    private TextView tv;
 
    //这个地方的handler最好封装 防止内存泄漏
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 100) {
                String res = (String) msg.obj;
                tv.setText(res);
                Toast.makeText(getApplicationContext(), res, Toast.LENGTH_SHORT).show();
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_four); 
        tv = (TextView) findViewById(R.id.tv1);
        btn = (Button) findViewById(R.id.btn1);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000); //等待5秒,模拟耗时操作,比如下载
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Message msg = new Message();
                        msg.what = 100;  //消息发送的标志
                        msg.obj = "MainActivity click button"; //消息发送的内容,如:Object、String、类、int
                        handler.sendMessage(msg);
                    }
                }).start();
            }
        });
    }
}

 

 

 

 

 

 

 

posted @ 2022-07-08 16:36  熊猫Panda先生  阅读(2209)  评论(0编辑  收藏  举报