Android Android Studio 3.3 项目中导入环信的 EaseUI Module

一、前言

最近看到有许多人在说根据环信官方文档导入 EaseUI 会出现各种错误,由于官方(此处和谐省略一万字), 顺便学学新的 add dependencies 方式,记录一下, Android Studio 3.3 add dependencies 方式又改了。

二、运行环境

Android Studio 版本 : 3.3 Canary 5

!Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第1张图片

环信 SDK 版本 : easemob-sdk-3.5.0

三、导入 EaseUI

先新建个项目

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第2张图片

下载 easemob-sdk 里面带有 EaseUI

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第3张图片

导入这个 easeui

File -> New -> Import Module

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第4张图片

选择 easeui 所在的路径

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第5张图片

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第6张图片

点击 Finish

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第7张图片

导入之后会有红色的提示,四个都只是 WARNING,把四个 WANRNING 解决掉

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第8张图片

第一个 WANRNING,是 Build Tools 的问题,Android Studio 3.3.0-alpha05 最低支持的 Build Tools 版本是 27.0.3 ,把 easeui 的 build.gradle 的 buildToolsVersion 改为 27.0.3 顺便把 compileSdkVersion 和 targetSdkVersion 也改为 27,同步一下 gradle,第一个 WANRNING 已经没了

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第9张图片

后三个 WANRNING,是 AndroidStudio 3.0 之后的问题,
compile 改为 implementation 或 api,
implementation 和 api 是有区别的,implementation 是指这个导入的 Library 是 Module 内部使用的,别的 Module 导入当前这个 Module 就用不了当前 Module 导入的 Library 的东西,api 是指别的 Module 导入当前这个 Module 后仍然能够使用当前 Module 导入的 Library 的东西,
testCompile 改为 testImplementation,
androidTestCompile 改为 androidTestImplementation,

由于 easeui 的 libs 文件夹里有环信的 hyphenatechat_3.5.0.jar ,由于 app 里要用到 hyphenatechat 里的东西,所以 fileTree() 要用 api 而不是 implementation,改完后同步一下 gradle,剩下的三个 WANRNING 都没了

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第10张图片

接下来 app 添加上 easeui 这个 Module ,在项目右键 Open Module Settings

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第11张图片

选择 Dependencies,Modules 选择 app,Declared dependencies 点击 + 号

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第12张图片

上面 step 1 的 easeui 打上勾,Step 2 选 implementation,点击 OK,再点 Apply 或 OK

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第13张图片

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第14张图片

由于 AndroidStudio 3.2 起开始推荐使用 androidx, 但 easeui 里用的是 android, 所以会报错,要么把 easeui 里的改为 androidx 要么不使用 androidx,改 easeui 的话那就太多要改的了,不使用 androidx 了,

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第15张图片

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第16张图片

打开 gradle.properties 注释掉 android.useAndroidX=true 和 android.enableJetifier=true

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第17张图片

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第18张图片

打开 app 的 build.gradle 也要改,compileSdkVersion, buildToolsVersion, targetSdkVersion 那些改和 easeui 一样的,dependencies androidx 的改为 android 的,改完同步一下 gradle, 由于去掉了 androidx, Activity 里的 AppCompatActivity 和 布局里的 ConstraintLayout 也要改为 android 的而不是 androidx 的

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第19张图片

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第20张图片

看起来好像没什么问题了,先运行一下,

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第21张图片

出问题了,support v4 包没有 AsyncTaskCompat 这个类了,打开 EaseChatRowImage.java 这个类

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第22张图片

去掉 AsyncTaskCompat,改直接执行 AsyncTask ,

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第23张图片

再运行一下,可以运行起来

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第24张图片

四、初始化 easeui

虽然导入了 easeui, 但 easeui 还没在 app 里初始化和用起来,先初始化 easeui
新建个 App 类继承 Application,然后在 AndroidManifest 里设置

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initEaseUi();
    }

    private void initEaseUi() {
        EMOptions emOptions = new EMOptions();
        emOptions.setAcceptInvitationAlways(false);
        EaseUI.getInstance().init(this, new EMOptions());
    }
}

再运行一次 , 报错了,看下 Log

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第25张图片

说没有在 AndroidManifest 里设置 APPKEY,那就设置一下了,可参考官方的 环信官方配置工程


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.ce.easeuitest">

    
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">

        
        <meta-data android:name="EASEMOB_APPKEY"  android:value="1111180606228105#easeuitest" />
        
        <service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/>
        <service android:name="com.hyphenate.chat.EMJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true"
            />
        
        <receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_REMOVED"/>
                <data android:scheme="package"/>
            intent-filter>
            
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.USER_PRESENT" />
            intent-filter>
        receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

manifest>

再运行一次, 可以运行起来,再看 log 没有问题

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第26张图片

五、简单使用 easeui

做个登录功能测试一下,新建个 LoginActivity

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第27张图片

修改一下 LoginActivity 的代码逻辑并实现登录操作,为了直接点,这里就在 Activity 里去开启线程做登录操作了

/**
 * A login screen that offers login via email/password.
 */
public class LoginActivity extends AppCompatActivity{
    // UI references.
    private AutoCompleteTextView mEmailView;
    private EditText mPasswordView;
    private View mProgressView;
    private View mLoginFormView;
    private Handler mHandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        // Set up the login form.
        mEmailView = (AutoCompleteTextView) findViewById(R.id.email);

        mPasswordView = (EditText) findViewById(R.id.password);
        mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
                if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
                    attemptLogin();
                    return true;
                }
                return false;
            }
        });
        Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
        mEmailSignInButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                attemptLogin();
            }
        });

        mLoginFormView = findViewById(R.id.login_form);
        mProgressView = findViewById(R.id.login_progress);
    }
    @Override
    protected void onDestroy() {
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
    /**
     * Attempts to sign in or register the account specified by the login form.
     * If there are form errors (invalid email, missing fields, etc.), the
     * errors are presented and no actual login attempt is made.
     */
    private void attemptLogin() {
        // Reset errors.
        mEmailView.setError(null);
        mPasswordView.setError(null);
        // Store values at the time of the login attempt.
        String email = mEmailView.getText().toString();
        String password = mPasswordView.getText().toString();
        boolean cancel = false;
        View focusView = null;
        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
            mPasswordView.setError(getString(R.string.error_invalid_password));
            focusView = mPasswordView;
            cancel = true;
        }
        // Check for a valid email address.
        if (TextUtils.isEmpty(email)) {
            mEmailView.setError(getString(R.string.error_field_required));
            focusView = mEmailView;
            cancel = true;
        } else if (!isEmailValid(email)) {
            mEmailView.setError(getString(R.string.error_invalid_email));
            focusView = mEmailView;
            cancel = true;
        }
        if (cancel) {
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView.requestFocus();
        } else {
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true);
            // 开启线程去做登录操作
            new LoginThread(email, password).start();
        }
    }
    private boolean isEmailValid(String email) {
        return email.length() > 4;
    }
    private boolean isPasswordValid(String password) {
        return password.length() > 4;
    }
    /**
     * Shows the progress UI and hides the login form.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private void showProgress(final boolean show) {
        // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
        // for very easy animations. If available, use these APIs to fade-in
        // the progress spinner.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);

            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
            mLoginFormView.animate().setDuration(shortAnimTime).alpha(
                    show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
                }
            });

            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
            mProgressView.animate().setDuration(shortAnimTime).alpha(
                    show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
                }
            });
        } else {
            // The ViewPropertyAnimator APIs are not available, so simply show
            // and hide the relevant UI components.
            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
        }
    }

    private class LoginThread extends Thread {
        String username;
        String password;
        public LoginThread(String username, String password) {
            this.username = username;
            this.password = password;
        }
        @Override
        public void run() {
            EMClient.getInstance().login(username, password, new EMCallBack() {
                @Override
                public void onSuccess() {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            showProgress(false);
                            // 登录成功跳转去 MainActivity
                            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            startActivity(intent);
                            finish();
                        }
                    });
                }
                @Override
                public void onError(int i, String s) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            // 登录失败
                            showProgress(false);
                            mPasswordView.setError(getString(R.string.error_invalid_password));
                            View focusView = mPasswordView;
                            focusView.requestFocus();
                        }
                    });
                }
                @Override
                public void onProgress(int i, String s) {
                }
            });
        }
    }
}

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第28张图片

修改下 Manifest,让 LoginActivity 先运行

<activity
    android:name=".LoginActivity"
    android:label="@string/title_activity_login">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    intent-filter>
activity>

由于登录后跳转到 MainActivity ,那就简单地在 MainActivity 做个联系人列表显示,
新建个 ContactFragment 类继承 EaseContactListFragment,这里也简单点,用个 AsyncTask 去获取所有的联系人然后显示出来,

public class ContactFragment extends EaseContactListFragment {
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        new GetContact().execute();
    }
    private class GetContact extends AsyncTask<Void, Void, Map<String, EaseUser>> {
        @Override
        protected Map doInBackground(Void... voids) {
            try {
                Map contactMap = new HashMap<>();
                List contacts = EMClient.getInstance().contactManager().getAllContactsFromServer();
                for (String contact : contacts) {
                    EaseUser easeUser = new EaseUser(contact);
                    contactMap.put(contact, easeUser);
                }
                return contactMap;
            } catch (HyphenateException e) {
                e.printStackTrace();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Map easeUserMap) {
            if (easeUserMap != null && !easeUserMap.isEmpty()) {
                setContactsMap(easeUserMap);
                refresh();
            }
        }
    }
}

把 ContactFragment 放到 MainActivity 去显示,

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.lay_content, new ContactFragment())
                .commitNowAllowingStateLoss();
    }
}

一切准备就绪,等等,还有账号还没准备,先注册几个账号,

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第29张图片

添加几个好友进去

Android Android Studio 3.3 项目中导入环信的 EaseUI Module_第30张图片

运行起来看看吧,可以登录并获取到所有联系人

六、后记

导入 easeui 确实有几个坑的地方,不过没多坑,Android Studio 3.3 的 add dependencies 方式感觉更加友好了。

本文源码

你可能感兴趣的:(Android,开发)