Android之手机卫士涉及的知识点总结

本文总结了Android手机卫士开发中的关键知识点,包括Splash界面、自动更新、状态选择器、自定义组件、对话框、动画效果、权限管理、数据库操作、进程管理、广播接收者、短信拦截、内存优化、安全机制等。详细阐述了如何实现一键锁屏、卸载、启动应用、获取位置信息、短信备份、防病毒等功能,并探讨了应用的性能优化和用户体验提升策略。
摘要由CSDN通过智能技术生成

手机卫士涉及的知识点总结

Splash界面

splash: 溅,洒

  • 展现产品的logo提升产品的知名度
  • 初始化操作(创建数据库,读取配置文件)
  • 连接服务器检查软件授权
  • 连接服务器检查软件的更新

自动更新的前提

  • 包名一致
  • 签名一致

状态选择器

整体取消掉标题栏:在清单文件中加一修改主题

android:theme=”@android:style/Theme.Light.NoTitleBar”

PackageManager:获取各种包的信息(版本、应用程序图标、包信息等)

开源项目框架:

xUtils-2.6.8.jar 断点下载

使用:

HttpUtils httputils=new HttpUtils();
httputils.download(url,target,autoResume,callback);
//url:下载的路径
//targer:存放的路径sd
//autoResume:true是否断点续传
//callback:下载回传
new RequestCallBack<File>(){
    重写onSuccess();
    重写onFailure();
    重写onload();//显示下载进度在textview中当前/总进度
}

开源项目断点下载xUtils耗时操作

            HttpUtils http=new HttpUtils();
            final File file=new File(Environment.getExternalStorageDirectory(),"xxx.apk");
            http.download(data.downLoadUrl, file.getAbsolutePath(), true, new RequestCallBack<File>(){
                //下载失败
                @Override
                public void onFailure(HttpException arg0, String arg1) {

                }
                //下载成功
                @Override
                public void onSuccess(ResponseInfo arg0) {
                    //下载成功,替换安装模板代码
                    ToastUtils.show(SplashActivity.this, "下载成功");
                    Intent intent=new Intent();
                    intent.setAction("android.intent.action.VIEW");
                    intent.addCategory("android.intent.category.DEFAULT");
                    intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                    startActivity(intent);

                }});

下载替换安装只要调用系统的应用就行:(模板代码)

Itent intent=new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
startActivity(intent);

市面上常用界面-九宫格

状态选择器:

就是在res目录下建立一个drawable文件中定义一个xml文件,设置属性background时引用这个xml文件就行。

//背景颜色选择

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

 <item android:drawable="@android:color/darker_gray" android:state_pressed="true">   //按住时
 <item android:drawable="@android:color/darker_gray" android:state_focused="true">  //聚焦时
 <item android:drawable="@android:color/transparent" >  //默认状态

</item>
</item>
</item>
</selector>

按住图标显示不一样的图片,新建一个tupian.xml文件,引用图标时R.drawable.tupian.xml

//图标状态选择

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

 <item android:drawable="@drawable/app_pressed" android:state_pressed="true">   //按住时
 <item android:drawable="@drawable/app_focused"
 android:state_focused="true">  //聚焦时
 <item android:drawable="@drawable/app" >   //默认状态

</item>
</item>
</item>
</selector>

走马灯效果按钮

Button可实现,当点击按钮的时候滚动起来
设置属性

android:focusableInTouchMode="true"
android:ellipsize="marquee"
android:text="显示的内容"

TextView也可以实现滚动走马灯:需要自定义TextView,实现里面的所有构造函数并重写isFocused()直接返回true

@Override
@ExportedProperty(category = "focus")
public boolean isFocused() {
    return true;
}

并设置下面几个属性

<com.cca.mobilephone.ui.FocusedTextView     
    android:ellipsize="marquee"
    android:focusableInTouchMode="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="true"
    android:text="希望我所认识的亲戚朋友远离病痛,远离饥饿,走上富康的道路,过上幸福的的生活!"
/>

这样textview跑马灯效果就可以跑起来了

设置一条线可以使用View

自定义组合控件:textView+checkBox 下面还有一条分割线

public class SettingCheckView extends LinearLayout {

public SettingCheckView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initial(context);

    String bigtitle=attrs.getAttributeValue("http://schemas.android.com/apk/res/com.cca.mobilephone", "bigtitle");
    TextView tv_title=(TextView) findViewById(R.id.tv_ui_setting);
    tv_title.setText(bigtitle);

}
public SettingCheckView(Context context) {
    super(context);
    initial(context);
}
private void initial(Context context) {
    this.setOrientation(LinearLayout.VERTICAL);//
    this.addView(View.inflate(context, R.layout.ui_setting_view, null));
}
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/rl_set_update"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:background="@drawable/background" >

<TextView
    android:id="@+id/tv_ui_setting"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="10dp"
    android:text="开启自动更新"
    android:textSize="20sp" />

<CheckBox
    android:id="@+id/cb_set_update"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"
    android:layout_marginRight="20dp"
    android:clickable="false"
    android:focusable="false" />

<View
    android:layout_width="match_parent"
    android:layout_height="0.1dp"
    android:layout_margin="2dp"
    android:background="#88000000" />

</RelativeLayout>

使用时:

<com.cca.mobilephone.ui.SettingCheckView
   android:layout_width="match_parent"
  android:layout_height="wrap_content"
  mobilephone:bigtitle="我是功能2"
    />

自定义属性:

先声明属性命名空间

  xmlns:mobilephone="http://schemas.android.com/apk/res/com.cca.mobilephone"

在values定义一个attrs.xml文件,在里面声明功能

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SettingCheckView">
    <attr name="bigtitle" format="string"/>
</declare-styleable>
    //或者
 <declare-styleable name="SettingCheckView">
    <attr name="title" format="string"/>
</declare-styleable>

</resources>

做到这里就可以使用自定义组合控件了,功能可以设置文本内容,想增加其他的属性,在attrs中定义出来就可以使用了。

自定义对话框:

    AlertDialog.Builder builder=new Builder(context);
        View dialogview=View.inflate(context, R.layout.show_setup_dialog, null);
        builder.setView(view);
        builder.show();

//高低版本默认的背景色和字体颜色不一样、使高低版本保持一致的样式需
设置其背景色、文本字体色

AlertDialog.Builder builder=new Builder(MainActivity.this);
    View dialogview=View.inflate(context, R.layout.show_setup_dialog, null);
    AlertDialog dialog=builder.create();
    dialog.setView(dialogview,0,0,0,0);//设置对话框上下左右的距离
    dialog.show();

show_setup_dialog的xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="wrap_content"

android:orientation="vertical" >
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#BFFF01"
        android:text="设置密码"
        android:textSize="30dp"
        android:gravity="center"/>
    <EditText 
        android:gravity="center_horizontal"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:hint="请输入密码"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:inputType="textPassword"/>
    <EditText 
        android:gravity="center_horizontal"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:hint="请确定密码"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:inputType="textPassword"/>
    <LinearLayout 
        android:layout_width="300dp"
        android:layout_height="wrap_content">

        <Button 
            android:layout_width="0dp"
            android:layout_weight="1"
             android:background="@drawable/btn_background"
            android:text="取消"
            android:layout_marginRight="5dp"
            android:layout_height="wrap_content"/>
         <Button 
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_marginLeft="5dp"
             android:background="@drawable/btn_background"
            android:text="确定"
            android:layout_height="wrap_content"/>
    </LinearLayout>

</LinearLayout>

自定义背景选择器:btn_background.xml(要放在drawable文件下)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@android:color/holo_blue_light" android:state_pressed="true">
     <item android:drawable="@android:color/holo_blue_light" android:state_focused="true">
     <item android:drawable="@android:color/transparent" >

    </item></item></item>

</selector>

当有很多个界面有相同的方法,相同的布局时,都要存储数据时,可定义一个父类让其他的类来继承它

动画的切换效果:

//一句代码,必须要放在Activity或者finish()的后面
overridePendingTransition(R.anim.trans_next_in, R.anim.trans_next_out);

在res目录下建立一个anim文件夹:创建两个xml:trans_next_in.xml和trans_next_out.xml如下

<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="100%p"
    android:toXDelta="0"
    android:toYDelta="0"
    android:fromYDelta="0"
    android:duration="300">
</translate>


<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="-100%p"
android:toYDelta="0"
android:fromYDelta="0"
android:duration="300">
</translate>

手势识别器

1、先声明一个手势识别器

private GestureDetector  mGestureDetector;

2、初始化一个手势识别器

//2、初始化手势识别器
    mGestureDetector=new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener(){

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2,
                float velocityX, float velocityY) {
            /**
             * e1、手指触摸屏幕的一瞬间
             * e2、手指离开屏幕的一瞬间
             * velocityX、velocityY:水平方向和竖直方向的速度
             * 
             */
            if((e1.getRawX()-e2.getRawX())>150){
                showNext();
                overridePendingTransition(R.anim.trans_next_in, R.anim.trans_next_out);
                return true;
            }
            if((e2.getRawX()-e1.getRawX())>150){
                showPre();
                overridePendingTransition(R.anim.trans_pre_in, R.anim.trans_pre_out);
                return true;
            }

            return super.onFling(e1, e2, velocityX, velocityY);
        }

    });

3、第三步、使用户识别手势器的动作

//第三步、使用手势识别器识别用户的动作
@Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);
    return super.onTouchEvent(event);
}

绑定手机卡、获取手机序列号,一个手机号对应一个序列号

//用到一个系统的服务
private TelephonyManager tm=(TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
//获取手机序列号
String sim=tm.getSimSerialNumber();

电话联系人

data/data/com.android.providers.contacts.database.contacts2.db

对应三张表:

查找联系人工具类:

public class ContactsInfoUtils {

public static List<ContactsInfo> getContactsInfo(Context context){

List<ContactsInfo> infocontacts=new ArrayList<ContactsInfo>();
    //获取内容提供者的解析器
    ContentResolver resolver=context.getContentResolver();
    Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
    Uri dataUri=Uri.parse("content://com.android.contacts/data");
    //游标
    Cursor cursor=resolver.query(uri, new String[]{"contact_id"}, null, null, null);
    //遍历游标集合
    while(cursor.moveToNext()){
        String id=cursor.getString(0);
        System.out.println("id"+id);
        ContactsInfo infos=new ContactsInfoUtils().new ContactsInfo();
        Cursor datacursor=resolver.query(dataUri, new String[]{"data1"}, "raw_contact_id=?", null, null);
        while(datacursor.moveToNext()){
            String data1=datacursor.getString(0);
            String mimetype=datacursor.getString(1);
            if("vnd.android.cursor.item/name".equals(mimetype)){
                //姓名
                infos.name=data1;
            }else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
            //电话
                infos.phone=data1;
            }else if("vnd.android.cursor.item/email_v2".equals(mimetype)){
            //电话
            infos.email=data1;
            }
        }
        infocontacts.add(infos);
    }
    return infocontacts;

}
public class ContactsInfo{
    public String name;
    public String email;
    public String phone;
}
}

密码加密:md5 单向加密,不可逆原文–>密文

public static String encode(String text){

    try {
        MessageDigest digest=MessageDigest.getInstance("md5");
        String password="234";
        byte[] result=digest.digest(password.getBytes());
        StringBuffer sb=new StringBuffer();
        for(byte b:result){
            String hex=Integer.toHexString(b&0xff)+2;//加盐更好更安全
            if(hex.length()==1){
                sb.append("0");
            }
            sb.append(hex);
        }
        return sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return "";
    }
}

播放报警音乐:便捷方式:src下新建目录raw可放音乐文件

    MediaPlayer mediaplayer=MediaPlayer.create(context, R.raw.uri);
            //循环播放
            mediaplayer.setLooping(true);
            //设置音量最大声
            mediaplayer.setVolume(1.0f, 1.0f);

            mediaplayer.start();
            abortBroadcast();

超级管理员:

Android 2.2引入了支持企业应用程序提供Android设备管理API。设备管理API提供了设备管理功能在系统水平。这些api允许您创建安全性敏感的应用程序是有用的在企业环境中,IT专业人员需要丰富的控制员工的设备。例如,内置Android电子邮件应用程序利用了新的api来改善交流的支持。通过电子邮件应用程序,交流管理员可以执行密码策略——包括字母数字密码或数字针——在设备。管理员也可以远程擦除(即恢复工厂默认值)丢失或被盗的手机。用户可以同步他们的电子邮件和日历数据交换。
Email client
Security application that to remove wipe
Device management service and application

一键锁屏应用:能够一键锁屏,一键卸载

步骤:

1、先创建admim类继承DeviceAdminReceiver

2、配置清单文件(参考api文档)

 <receiver
        android:name="com.cca.yijian.Admin"
        android:description="@string/sample_device_admin_description"
        android:label="@string/sample_device_admin"
        android:permission="android.permission.BIND_DEVICE_ADMIN" >
        <!-- 元数据,提供设备的超级管理员的配置信息 -->
        <meta-data
            android:name="android.app.device_admin"
            android:resource="@xml/device_admin_sample" />

        <intent-filter>
            <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
        </intent-filter>
    </receiver>

还要新建一个 res目录下xml文件夹并新建device_admin_sample.xml:
声明中使用的安全策略的元数据提供了特定于设备管理员的附加信息,可通过DeviceAdminInfo类进行解析查看,以下为device_admin_sample.xml:的内容

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<watch-login />   
<reset-password />
<force-lock />
<wipe-data />
<expire-password />
<encrypted-storage />
<disable-camera />
 </uses-policies>
</device-admin>

3、主活动中书写代码

点击按钮一键锁屏:

  public void lockscreen(View view){
    DevicePolicyManager dpm=(DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
    ComponentName who=new ComponentName(this, Admin.class);
    if(dpm.isAdminActive(who)){
        //重置密码
        //dpm.resetPassword("123", 0);
        //清除sd卡数据
        //dpm.wipeData(DevicePolicyManager.WIPE_EXTERNAL_STORAGE);

        dpm.lockNow();
        finish();
    }else{
        Toast.makeText(this, "请先激活应用程序", 0).show();
    }

}

使用时:先来到系统设置界面,找到安全、进入设备管理器、找到一键锁屏,点击激活一键锁屏,此时可以使用了。

先激活应用程序

给用户一个很好的体验:来个按钮“先激活应用程序”

    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
     ComponentName who=new ComponentName(this, Admin.class);

    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, who);
     intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,"请大家赶紧去激活程序吧,首次激活有大礼包!");
     startActivity(intent);

一键卸载

再来个卸载按钮:

public void deleteLockScreen(View view){
    DevicePolicyManager dpm=(DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
     ComponentName who=new ComponentName(this, Admin.class);
    dpm.removeActiveAdmin(who);

    Intent intent=new Intent();
    intent.setAction(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+getPackageName()));
    startActivity(intent);
}

获取位置的经纬度(真实标准坐标)在中国要转换成火星坐标才能真正确定位置

 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //获取位置内容提供者
    LocationManager lm=(LocationManager) getSystemService(Context.LOCATION_SERVICE);

    Criteria criteria=new Criteria();
    //指定高精度
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    //指定高耗电量
    criteria.setPowerRequirement(Criteria.POWER_HIGH);
    //获取最好的内容提供者
   String provider =lm.getBestProvider(criteria, true);

    lm.requestLocationUpdates(provider, 0, 0, new LocationListener() {
        //状态改变时调用
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {   
        }
        //可用时调用
        @Override
        public void onProviderEnabled(String provider) {
        }   
        @Override
        public void onProviderDisabled(String provider) {

        }       
        @Override
        public void onLocationChanged(Location location) {      
            location.getLatitude();//纬度
            location.getLongitude();//经度
            System.out.println("纬度:"+location.getLatitude()+"------经度:"+location.getLongitude());

            TextView text=new TextView(getApplication());
            text.setTextColor(Color.RED);
            text.setText("纬度:"+location.getLatitude()+"------经度:"+location.getLongitude());

        }
    } );

}

标准坐标—->>中国的火星坐标

使用算法:导入modifyOffset.java和axisoffset.dat

直接到代码中使用:

ModifyOffset no =ModifyOffset.getInstance(context.getClassLoader().getResourceAsStream("axisoffset.dat"));

//真实坐标X经度 Y为纬度
//不过查找时外国网站经度放在围纬度后面:纬度,经度

PointDouble pt =new PointDouble(x,y);
PointDouble marpoint=mo.s2c(pt);

System.out.println(marpoint.toString());
//输出的是x=。。。。,y=。。。。。经纬度

listView的简单优化

容易内存溢出

1、尽量复用convertview历史的缓存,减少创建新的view对象

2、尽量的减少子孩子的id的查询次数,定义一个viewHolder

View view;
viewHolder holder;
if(convertView!=null){
    //复用历史的view对象
    view=convertView;
    holder=(viewHolder) view.getTag();
}else{
    //创建新的孩子时加上标签
     holder=new viewHolder();
     view=View.inflate(getApplicationContext(), R.layout.item_callsmssafe, null);
     holder.black_phone=(TextView) view.findViewById(R.id.tv_black_phone);
     holder.black_mode=(TextView) view.findViewById(R.id.tv_black_mode);
     view.setTag(holder);

}
//内部类
class viewHolder{
    public TextView black_phone;
    public TextView black_mode;
    public ImageView black_delete;
}

listView显示数据库中的数据时,当listview发生变化时应更新listview数据

//通知listview更新数据
adapter.notifyDataSetChanged();

在清单文件中配置广播接收者的特点是:不管应用程序进程是否存在都能接受到对应广播

短信拦截:开启服务并在里面注册一个广播接收者

开启服务:
Intent intent=new Intent(SettingActivity.this,CallSmsSafeService.class);
    startService(intent);

//服务里面代码动态注册一个广播接收者

public class CallSmsSafeService extends Service {
    private BlackNumberDao dao;
    private InnerReceiver receiver;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        dao=new BlackNumberDao(this);
    //动态注册
        receiver=new InnerReceiver();
        IntentFilter filter=new IntentFilter();
        //设置关心短信到来的动作
        filter.addAction("android.provider.Telephony.SMS_RECEIVED");
        filter.setPriority(Integer.MAX_VALUE);
        //代码注册广播接收者
        registerReceiver(receiver, filter);
        System.out.println("黑名单短信拦截开启了!!!!!");
        super.onCreate();

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
        receiver=null;
    }
    //内部类广播接收者
    private class InnerReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            //拦截短信
            Object[] objs=(Object[]) intent.getExtras().get("pdus");
            for(Object obj:objs){
                SmsMessage smsMessage=SmsMessage.createFromPdu((byte[])obj);
                String address=smsMessage.getOriginatingAddress();
                String result=dao.find(address);
                if("2".equals(result)||"3".equals(result)){
                    System.out.println("黑名单短信拦截模式。。。");
                    abortBroadcast();
                }
                //智能拦截
                String body=smsMessage.getMessageBody();
                if(body.contains("天使")){//分词算法
                    SmsManager.getDefault().sendTextMessage("13531829360", null, "帮你拦截了天使客一条消息", null, null);

                    abortBroadcast();
                }
            }
        }
    }
}

服务断电就会自动停止,用sp存储不会保存状态,读取系统的的运行信息,调用系统的活动和服务管理者ActivityManager可以判断服务是否正在后台运行

/**
 * 判断系统的服务是否在后台运行
 * context:上下文
 * StringName: 服务的全路经名
 */
public static boolean isServiceRunning(Context context,String StringName){

    ActivityManager am=(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningServiceInfo> infos=am.getRunningServices(100);
    for(RunningServiceInfo info:infos){
            String className=info.service.getClassName();
            if(StringName.equals(className)){
                return true;
            }
    }
    return false;
}

分词算法:
开源算法:
luncence

利用反射原理挂断电话

    /**
     * 挂断电话的方法,利用反射
     */
    public void endCall() {
        try {
            Class clazz=CallSmsSafeService.class.getClassLoader().loadClass("android.os.ServiceManager");
            Method method=clazz.getDeclaredMethod("getService", String.class);
            IBinder ibinder=(IBinder) method.invoke(null, TELEPHONY_SERVICE);
            ITelephony iTelephony=ITelephony.Stub.asInterface(ibinder);
            iTelephony.endCall();


        } catch (E
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值