当前位置: 首页 > news >正文

公司建设网站属于什么费用网站打模块

公司建设网站属于什么费用,网站打模块,重庆做网站seo优化选哪家好,什么网站做美式软装设计方案IPC 为了弄懂IPC的来龙去脉#xff0c;我将从以下三个方面为大家来讲解#xff0c;希望对大家理解IPC会有帮助 什么是IPC IPC是Inter Process Communication的缩写#xff0c;其意思就是进程间的通信#xff0c;也就是两个进程之间的通信过程。我们都知道在Android系统中我将从以下三个方面为大家来讲解希望对大家理解IPC会有帮助 什么是IPC IPC是Inter Process Communication的缩写其意思就是进程间的通信也就是两个进程之间的通信过程。我们都知道在Android系统中每个应用都运行在一个进程上具有自己的DVM实例而且进程之间是相互隔离的也就是说各个进程之间的数据是互相独立互不影响的而如果一个进程崩溃了也不会影响到另一个进程。 采取这样的设计是有一定道理的例如这样的前提下将互相不影响的系统功能分拆到不同的进程里面去有助于提升系统的稳定性毕竟我们都不想自己的应用进程崩溃会导致整个手机系统的崩溃。 进程之间隔离是不错的选择可是如果进程之间想要互相通信进行数据交互的时候那该怎么办呢例如我们在自己的应用中想要访问手机通讯录中的联系人很显然这是两个不同的进程如果Android没有提供一种进程之间交流的机制那么这种功能将无法实现。 不过由于Android系统使用的是Linux内核而在Linux系统中进程之间的交互是有一套机制的所以Android也借鉴了其中的一些机制从而形成了Android的IPC机制。 上面只是粗略的讲解了IPC是啥关于它的使用和原理我将一一为大家呈上。 为什么要用IPC 上一点中我举了访问手机通讯录的例子。但你可能觉得我不需要用到这种功能那么我就不用管IPC啦其实不然IPC在我们的应用开发过程中随处可见下面我将举一个例子来说明他的重要性。 我们在MainActivity中修改一个静态变量接着在另一个进程的SecondActivity中去访问该变量看看能否读取已经修改过的变量。 1、新建一个Student类并声明一个静态变量 public class Student {    public static String nameBOB; } 2、在MainActivity的onCreate方法中修改name的值并打印log Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);Student.name  JACK;Log.d(MainActivity:Sname, Student.name); } 3、将SecondActivity设置为新进程并在其onCreate方法中访问name !-- 在清单文件中通过android:process属性为SecondActivity指定特定的进程com.bob.aidltestsecond --activity     android:name.SecondActivity    android:process:second/activity public class SecondActivity extends AppCompatActivity {    Overrideprotected void onCreate(Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.second_activity);Log.d(SecondActivity:Sname , Student.name);} } 4、通过log可以看到在MainActivity中修改了name的值但是在SecondActivity中却无法读取修改后的值 通过以上的实验大家应该明白了一点在不同的进程之间访问同一个静态变量是行不通的。其原因是每一个进程都分配有一个独立的虚拟机不同的虚拟机在内存分配上有不同的地址空间这就导致在不同的虚拟机上访问同一个对象会产生多个副本。例如我们在MainActivity中访问的name的值只会影响当前进程而对其他进程不会造成影响所以在SecondActivity中访问name时依旧只能访问自己进程中的副本。 Android中解决IPC的方法 上面也讲到为了解决这些跨进程的问题Android沿用了一些Linux的进程管理机制使得进程之间能够进行交互下面我将列出一些常见的IPC方式需要指出的是本文主要讲解Binder机制所以会注重讲解AIDL其他方式请读者自行查阅相关资料。 名称特点使用场景Bundle只能传输实现了Serializable或者Parcelable接口或者一些Android支持的特殊对象适合用于四大组件之间的进程交互文件不能做到进程间的即时通信并且不适合用于高并发的场景适合用于SharedPreference以及IO操作ContentProvider可以访问较多的数据支持一对多的高并发访问因为ContentProvider已经自动做好了关于并发的机制适合用于一对多的数据共享并且需要对数据进行频繁的CRUD操作Socket通过网络传输字节流支持一对多的实时通信但是实现起来比较复杂适合用于网络数据共享Messenger底层原理是AIDL只是对其做了封装但是不能很好的处理高并发的场景并且传输的数据只能支持Bundle类型低并发的一对多的即时通信AIDL功能强大使用Binder机制(接下来会讲解),支持一对多的高并发实时通信但是需要处理好线程同步一对多并且有远程进程通信的场景Binder 终于来到这篇文章的重头戏了上面讲到Android解决IPC的方法中有一种是AIDL它使用的原理就是Binder只有理解了Binder我们才算是理解了Android跨进程通信的原理。在这里我会带大家看看Android中有哪一些重要的地方使用到了Binder接着我们会通过一个实例来了解如何使用Binder最后我们会分析Binder的源码来理解他的工作流程。 Binder在Android中的运用 说起Binder在Android的使用场景可以说是无处不在我列出一些最常见的场景 四大组件的生命周期都是使用Binder机制进行管理的 View的工作原理也使用了Binder WindowManager的工作机制同样使用了Binder 以上三个方面只是最常见的场景但是却几乎包括了我们开发的整个流程。我们开发的应用都离不开四大组件而四大组件也正是依靠Binder机制运行的对于我们最常见的View他是如何显示的View又是如何响应我们的动作的这其中也用到了Binder(关于这些内容我会在后续的文章中为大家分析)。可以说了解Binder对于我们的开发是很有帮助的那接下来我们就来看看我们该如何使用Binder进行进程间的通信吧 如何使用Binder 现在我们需要实现这样的功能客户端与服务端位于不同的进程客户端需要向服务端添加学生同时客户端还可以向服务端发起查询学生列表的请求。 1、创建Student.javaStudent.aidlIStudentManager.aidl Student.java package com.bob.aidltest.aidl;import android.os.Parcel;import android.os.Parcelable;/** * Created by bob on 17-7-3. * 所有需要在Binder传递的数据类型都需要实现Parcelable接口 */public class Student implements Parcelable{    public static String nameBOB;    public int s_id;    public String s_name;    public String s_gender;    public Student(Parcel in) {s_id  in.readInt();s_name  in.readString();s_gender  in.readString();}    public Student(int s_id, String s_name, String s_gender) {        this.s_id  s_id;        this.s_name  s_name;        this.s_gender  s_gender;}    Overridepublic int describeContents() {        return 0;}    Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(s_id);dest.writeString(s_name);dest.writeString(s_gender);}    public static final CreatorStudent CREATOR  new CreatorStudent() {        Overridepublic Student createFromParcel(Parcel in) {            return new Student(in);}        Overridepublic Student[] newArray(int size) {            return new Student[size];}};    Overridepublic String toString() {        return String.format([StudentID: %s , StudentName: %s , StudentGender: %s], s_id, s_name, s_gender);} } Student.aidl // Student1.aidlpackage com.bob.aidltest.aidl;parcelable Student; IStudentManager.aidl // IStudentManager.aidlpackage com.bob.aidltest.aidl;import com.bob.aidltest.aidl.Student;interface IStudentManager {    ListStudent getStudentList();    void addStudent(in Student student); } 创建完毕之后手动编译项目(Build--ReBuild Project)接着就会在app/build/generated/source/aidl/debug/com/bob/aidltest/aidl/IStudentManager.java中看到自动生成的IStudentManager接口如下图 2、分析IStudentManager.java 先来看看自动生成的代码 public interface IStudentManager extends android.os.IInterface{    /** 内部类Stub继承自Binder并且实现了IStudentManager接口因此他也是一个Binder对象这个内部类是需要在服务端手动实现的并且会通过onBind方法返回给客户端 */public static abstract class Stub extends android.os.Binder implements com.bob.aidltest.aidl.IStudentManager{        private static final java.lang.String DESCRIPTOR  com.bob.aidltest.aidl.IStudentManager;        /** 构造方法 */public Stub(){            this.attachInterface(this, DESCRIPTOR);}        /**         * 将服务端的Binder对象转换为客户端的所需的AIDL接口类型的对象客户端拿到这个对象就可以通过这个对象远程访问服务端的方法         */public static com.bob.aidltest.aidl.IStudentManager asInterface(android.os.IBinder obj){            if ((objnull)) {                return null;}android.os.IInterface iin  obj.queryLocalInterface(DESCRIPTOR);            if (((iin!null)(iin instanceof com.bob.aidltest.aidl.IStudentManager))) {                return ((com.bob.aidltest.aidl.IStudentManager)iin);}            return new com.bob.aidltest.aidl.IStudentManager.Stub.Proxy(obj);}        Override public android.os.IBinder asBinder(){            return this;}        /**         * 运行在服务端进程的Binder线程池中当客户端进程发起远程请求时远程请求会要求系统底层执行回调该方法         * param code 客户端进程请求方法标识符。服务端进程会根据该标识确定所请求的目标方法         * param data 目标方法的参数他是客户端进程传进来的当我们调用addStudent(Student student)方法时参数就是Student对象         * param reply 目标方法执行后的结果将会返回给客户端例如当我们调用getStudentList返回的就是一个Student的列表         */Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{            switch (code){                case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);                    return true;}                case TRANSACTION_getStudentList:{data.enforceInterface(DESCRIPTOR);java.util.Listcom.bob.aidltest.aidl.Student _result  this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);                    return true;}                case TRANSACTION_addStudent:{data.enforceInterface(DESCRIPTOR);com.bob.aidltest.aidl.Student _arg0;                    if ((0!data.readInt())) {_arg0  com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);}                    else {_arg0  null;}                    this.addStudent(_arg0);reply.writeNoException();                    return true;}}            return super.onTransact(code, data, reply, flags);}        /**         * 代理的内部类他实现了IStudentManager接口这个代理类就是服务端返回给客户端的AIDL接口对象客户端可以通过这个代理类访问服务端的方法         */private static class Proxy implements com.bob.aidltest.aidl.IStudentManager{            private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote  remote;}            Override public android.os.IBinder asBinder(){                return mRemote;}            public java.lang.String getInterfaceDescriptor(){                return DESCRIPTOR;}            Override public java.util.Listcom.bob.aidltest.aidl.Student getStudentList() throws android.os.RemoteException{android.os.Parcel _data  android.os.Parcel.obtain();android.os.Parcel _reply  android.os.Parcel.obtain();java.util.Listcom.bob.aidltest.aidl.Student _result;                try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);_reply.readException();_result  _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);}                finally {_reply.recycle();_data.recycle();}                return _result;}            Override public void addStudent(com.bob.aidltest.aidl.Student student) throws android.os.RemoteException{android.os.Parcel _data  android.os.Parcel.obtain();android.os.Parcel _reply  android.os.Parcel.obtain();                try {_data.writeInterfaceToken(DESCRIPTOR);                    if ((student!null)) {_data.writeInt(1);student.writeToParcel(_data, 0);}                    else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);_reply.readException();}                finally {_reply.recycle();_data.recycle();}}}        static final int TRANSACTION_getStudentList  (android.os.IBinder.FIRST_CALL_TRANSACTION  0);        static final int TRANSACTION_addStudent  (android.os.IBinder.FIRST_CALL_TRANSACTION  1);}    public java.util.Listcom.bob.aidltest.aidl.Student getStudentList() throws android.os.RemoteException;    public void addStudent(com.bob.aidltest.aidl.Student student) throws android.os.RemoteException; } 可能看了上面的注释大家还是一头雾水那就先看看这个类的结构图吧 有关这个类的细节我们待会讲现在只需要知道我们需要在服务端手动实现Proxy类并实现其中的方法。 创建StudentManagerService.java并为其指定进程 /** * Created by bob on 17-7-3. * 服务端代码 */public class StudentManagerService extends Service {    private static final String TAG  StudentManagerService;    //判断Service是否销毁private AtomicBoolean mIsServiceDestroyed  new AtomicBoolean(false);    //适合用于进程间传输的列表类private CopyOnWriteArrayListStudent mStudentList  new CopyOnWriteArrayListStudent();    Overridepublic void onCreate() {        super.onCreate();        //在服务端手动添加两位默认的学生mStudentList.add(new Student(1, BOB, man));mStudentList.add(new Student(2, MAY, woman));}    Overridepublic IBinder onBind(Intent intent) {        return mBinder;}    Overridepublic void onDestroy() {mIsServiceDestroyed.set(false);        super.onDestroy();}    private Binder mBinder  new IStudentManager.Stub() {        Overridepublic ListStudent getStudentList() throws RemoteException {SystemClock.sleep(5000);//休眠5s模拟耗时操作return mStudentList;}        Overridepublic void addStudent(Student student) throws RemoteException {mStudentList.add(student);}};} 在清单文件中指定服务的进程 service     android:name.StudentManagerService    android:process:remote/service 可以看到这个服务类跟普通的服务类相差并不大唯一的区别在于它创建了一个IStudentManager.Stub的匿名内部类并且实现了其中的方法在onBind方法中将这个IBinder对象返回给客户端。这里需要说明一下Binder是实现了IBinder接口的所以他同时也是一个IBinder对象。 在客户端愉快的绑定Service吧 public class MainActivity extends AppCompatActivity {    private static final String TAG  MainActivity_Client;    private static final int MESSAGE_QUERY_STUDENTLIST1;    private int student_size  3;    private IStudentManager mRemoteStudentManager;    private ServiceConnection mConnectionnew ServiceConnection() {        //onServiceConnected与onServiceDisconnected都是在主线程中的所以如果里面如果涉及到服务端的耗时操作那么需要在子线程中进行Overridepublic void onServiceConnected(ComponentName name, IBinder service) {            //获取到IStudentManager对象final IStudentManager studentManager IStudentManager.Stub.asInterface(service);mRemoteStudentManager  studentManager;}        Overridepublic void onServiceDisconnected(ComponentName name) {mRemoteStudentManager  null;Log.d(TAG, onServiceDisconnected.threadname:  Thread.currentThread().getName());}};    Overrideprotected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);Intent intent  new Intent(this, StudentManagerService.class);        bindService(intent, mConnection, BIND_AUTO_CREATE);}    Overrideprotected void onDestroy() {        unbindService(mConnection);        super.onDestroy();}    //将服务端返回的数据显示在界面上private Handler mHandlernew Handler(){        Overridepublic void handleMessage(Message msg) {            switch (msg.what) {                case MESSAGE_QUERY_STUDENTLIST:Toast.makeText(MainActivity.this, msg.obj.toString(),Toast.LENGTH_SHORT).show();                default:                    super.handleMessage(msg);}}};    /**     * 在客户端向服务端添加一名学生     * param view     */public void addStudent(View view) {        if (mRemoteStudentManager ! null) {            try{                int student_id  student_size 1;Student newStudent;                if (student_id % 2  0) {newStudent new Student(student_id, 新学生  student_id, man);} else {newStudent new Student(student_id, 新学生  student_id, woman);}mRemoteStudentManager.addStudent(newStudent);Log.d(TAG, 添加一位学生  newStudent.toString());}catch(Exception e){e.printStackTrace();}}}    /**     * 在客户端向服务端发起查询学生的请求     * param view     */public void get_student_list(View view) {Toast.makeText(this, 正在获取学生列表, Toast.LENGTH_SHORT).show();        //由于服务端的查询操作是耗时操作所以客户端需要开启子线程进行工作new Thread(new Runnable() {            Overridepublic void run() {                if (mRemoteStudentManager ! null) {                    try{                        final ListStudent students  mRemoteStudentManager.getStudentList();student_size  students.size();Log.d(TAG, 从服务器成功获取到学生列表:  students.toString());mHandler.obtainMessage(MESSAGE_QUERY_STUDENTLIST, students).sendToTarget();}catch(Exception e){e.printStackTrace();}}}}).start();} } 可以看到我们在客户端只需要绑定远程的服务端服务端就会返回一个IBinder对象接着我们需要调用IStudentManager.Stub.asInterface()方法将这个IBinder对象转换为我们客户端可用的AIDL接口对象拿到这个对象之后我们就可以远程调用服务端的方法了。是不是很容易 但是需要注意的一点是为了模拟耗时操作我们在服务端的getStudentList的方法中使用休眠以模拟耗时操作所以客户端在调用该方法时不能直接在主线程中调用而是应该开启一个子线程在子线程中调用这个耗时的操作。 看看效果 首先我们获取学生列表接着连续添加4个学生再次查看学生列表最终的结果如下图可以看到我们已经实现了两个进程之间的交互接下来我们将分析Binder的原理。 Binder的原理 进程的机制 首先我们需要了解进程之间为什么不能直接进行通信以下是两个进程的示意图 从上面的图我们可以得到以下几点 一个进程空间分为用户态和内核态即把进程内用户和内核隔离开来 进程之间由于Android系统为每个进程分配了一个独立的虚拟机用户空间和内核空间的数据不可交互 Binder作为进程间的介质充当了中介使得进程间的内核态可以通过Binder进行数据交互 IPC交互示意图 图中总共有四个元素分别是充当客户端的Activity服务端的StudentManagerService充当服务管理者的IStudentManager以及充当访问介质的Binder驱动。他们的职责如下 StudentManagerService: 服务提供者这里面会有许多我们常用的服务在本例中提供的服务就是添加学生以及获取学生列表。而在系统中则包括有ActivityService 、 WindowMananger等服务这些系统服务提供的功能对四大组件以及Window的工作提供的保障。 Activity: 服务调用者一般就是我们的应用在这里我们通过调用StudentManagerService的服务来完成工作。 IStudentManager: 他是负责管理服务的在其内部通过map集合来存储Service与Binder的映射关系这样客户端在向其请求服务的时候就能够返回特定的Binder。 Binder驱动: 他是IStudentManager连接各种Service的桥梁同时也是客户端与服务端交流的桥梁。 总结起来说应用程序(Activity)首先向IStudentManager发送请求StudentManagerService的服务IStudentManager查看已经注册在里面的服务的列表找到相应的服务后通过Binder驱动将其中的Binder对象返回给客户端从而完成对服务的请求。 源码分析 我们主要分析的就是IStudentManager这个类从上面得到讲解我们已经知道它包含了两个类Stub和Proxy。先来看看Proxy类 //Proxy.javapublic java.util.Listcom.bob.aidltest.aidl.Student getStudentList() throws android.os.RemoteException{android.os.Parcel _data  android.os.Parcel.obtain();android.os.Parcel _reply  android.os.Parcel.obtain();java.util.Listcom.bob.aidltest.aidl.Student _result;    try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);_reply.readException();_result  _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);}    finally {_reply.recycle();_data.recycle();}    return _result; }public void addStudent(com.bob.aidltest.aidl.Student student) throws android.os.RemoteException{android.os.Parcel _data  android.os.Parcel.obtain();android.os.Parcel _reply  android.os.Parcel.obtain();    try {_data.writeInterfaceToken(DESCRIPTOR);        if ((student!null)) {_data.writeInt(1);student.writeToParcel(_data, 0);}        else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);_reply.readException();}    finally {_reply.recycle();_data.recycle();} } 上面截取了Proxy的两个方法其中Proxy是运行在客户端的他是用服务端返回来的Binder对象调用了public static IStudentManager asInterface(IBinder obj)方法返回来的。 既然Proxy运行在客户端那么客户端也是通过Proxy来调用远程服务端的方法的也就是说我们将调用方法需要用到的参数传递给Proxy接着由Proxy来访问服务端所以我们能够看到Proxy将我们的参数写进了_data而_reply则代表从服务端返回来的结果。 从代码中我们还看到客户端在将数据传递给服务端之后就处于阻塞状态直到服务端返回结果所以如果调用的服务端方法是一个耗时方法那么我们就需要在子线程中进行工作了。 数据准备好之后当然是需要传递了可以看到Proxy通过transact方法讲数据传递出去了接下来就来看transact方法 //Binder#transactpublic final boolean transact(int code, Parcel data, Parcel reply,            int flags) throws RemoteException {        if (false) Log.v(Binder, Transact:   code   to   this);        if (data ! null) {data.setDataPosition(0);}        //调用了Binder的onTransactboolean r  onTransact(code, data, reply, flags);        if (reply ! null) {reply.setDataPosition(0);}        return r;} 可以看到transact方法实际上调用了Binder的onTransact而这里的Binder就是指Stub了我们看一下Stub的定义 public static abstract class Stub extends android.os.Binder implements com.bob.aidltest.aidl.IStudentManager 可以看到Stub确实继承了Binder并且也实现了IStudentManager接口接下来我们继续看Stub中的onTransact方法 public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{    switch (code){        case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);            return true;}        case TRANSACTION_getStudentList:{data.enforceInterface(DESCRIPTOR);java.util.Listcom.bob.aidltest.aidl.Student _result  this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);            return true;}        case TRANSACTION_addStudent:{data.enforceInterface(DESCRIPTOR);com.bob.aidltest.aidl.Student _arg0;            if ((0!data.readInt())) {_arg0  com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);}            else {_arg0  null;}            this.addStudent(_arg0);reply.writeNoException();            return true;}}    return super.onTransact(code, data, reply, flags); } 可以看到服务端通过客户端传递过来的code常量来判断客户端需要调用的是哪个方法接着就执行该方法执行完之后如果有数据返回则将结果写入reply接着Proxy就可以收到结果了。而整个通信过程也就结束了。 最后我借用Carson_Ho的一张流程图来描述这个完整的流程 本文转自lzwxx 51CTO博客原文链接:http://blog.51cto.com/13064681/1944339
http://www.zqtcl.cn/news/186342/

相关文章:

  • 最好的网站设计公司源码 php多平台网站建设
  • 下载了网站源码施工企业质量管理体系应按照我国
  • 有关网站建设国内外现状的文献英文谷歌seo
  • 珠海做网站哪间好佛山网站建设骏域
  • 免费网站建设支持ftp网络规划设计师资格证
  • 网站打开文件按钮怎么做十大网络游戏
  • 问答类咨询网站的建设烟台开发区做网站
  • 网站域名费用怎么做分录销售crm客户管理系统
  • 海南住房与城乡建设网站大连做网站团队
  • 邯郸最穷的三个县长春纯手工seo
  • 昌黎网站建设贵德县建设局网站
  • 山西网站制作公司兼职做网站安全么
  • 阿里做网站怎么做青岛网站维护
  • 怎么建网站手机版郑州网站建设哪家好
  • 做企业网站有哪些好处安龙网站建设
  • 怎做连接网站wordpress iis设置方法
  • ugc网站开发网站设计常见流程
  • dz论坛可以做招聘网站国内空间没备案可以打开网站吗
  • 建设用地规划证查询网站公司起名字大全免费好听
  • 杭州网站建设公司有哪些瑞诺国际的数字营销模式
  • 宣城网站建设 有限公司高州做网站
  • 做外贸最适合的网站系统有可以做国外支付系统的网站吗
  • 建设执业资格注册中心网站办事大厅ui设计素材库
  • 个人网站免费建站4399电脑版网页链接
  • 重庆开县网站建设公司推荐网站建设与维护高职
  • 关于网站开发的技术博客海口网站设计建设
  • xx市院门户网站建设方案做视频特技的网站
  • 肇庆seo公司咨询23火星seo 网站
  • 天元建设集团有限公司破产新手seo网站做什么类型好
  • spa.net网站开发二次开发需要什么