Разрешения Android M: onRequestPermissionsResult () не вызывается


291

Я обновляю наше приложение, чтобы использовать новую систему разрешений M времени выполнения. Это все работает, кроме onRequestPermissionsResult (). Мне нужно проверить разрешение на нажатие кнопки и, если оно прошло успешно, отправить текстовое сообщение. Когда я даю разрешение на это, диалоговое окно закрывается, но оно не вызывает отправку текста, пока я снова не нажму кнопку.

Я отладил и установил точки останова в методе onRequestPermissionsResult (), но он никогда не входит в это.

Этот метод вызывается первым:

    private void askForPermission() {
    String[] permissions = new String[]{Manifest.permission.SEND_SMS};
    ActivityCompat.requestPermissions(getActivity(), permissions, PERMISSIONS_CODE);
}

И тогда мой обратный вызов выглядит так:

    @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == PERMISSIONS_CODE) {
        for (int i = 0; i < permissions.length; i++) {
            String permission = permissions[i];
            int grantResult = grantResults[i];

            if (permission.equals(Manifest.permission.SEND_SMS)) {
                if (grantResult == PackageManager.PERMISSION_GRANTED) {
                    onPPSButtonPress();
                } else {
                    requestPermissions(new String[]{Manifest.permission.SEND_SMS}, PERMISSIONS_CODE);
                }
            }
        }
    }
}

Кто-нибудь сталкивался с подобной проблемой? Ценю любую помощь с этим. Спасибо

Ответы:


576

Я столкнулся с той же проблемой, и я только что нашел решение. При использовании библиотеки поддержки вы должны использовать правильные вызовы методов. Например:

  • Находясь в AppCompatActivity , вы должны использовать ActivityCompat.requestPermissions ;
  • Находясь в android.support.v4.app.Fragment , вы должны просто использовать requestPermissions (это метод экземпляра android.support.v4.app.Fragment)

Если вы вызываете ActivityCompat.requestPermissions во фрагменте, обратный вызов onRequestPermissionsResult вызывается для действия, а не для фрагмента.

Надеюсь это поможет!


13
Это также происходит с действиями noHistory: code.google.com/p/android-developer-preview/issues/…
Энтони Бобенриет

11
«Когда в android.support.v4.app.Fragment вы должны просто использовать requestPermissions» - спасибо!
Тигран Саркисян

5
@AnthonyBobenrieth так, каково здесь решение для noHistory? Мы просто не можем запросить разрешения у них?
Дин Уайлд

2
Ссылка не работает @AnthonyBobenrieth. Есть ли какое-то решение без истории?
fersarr

3
При использовании Fragment.requestPermissions()родительская активность фактически получает onRequestPermissionsResult(). (Это с библиотекой поддержки 23.3.0)
Кто-то где-то

101

Вы можете попробовать это:

requestPermissions(permissions, PERMISSIONS_CODE);

Если вы вызываете этот код из фрагмента, у него есть собственный метод requestPermissions. Я считаю, что проблема в том, что вы вызываете статический метод.

Pro Совет, если вы хотите onRequestPermissionsResult()во фрагменте: FragmentCompat.requestPermissions(Fragment fragment, String[] permissions, int requestCode)


Спасибо за помощь. Я заметил, что есть метод requestPermissions, если он во фрагменте. Я нахожусь во фрагменте, и я попытался вызвать только requestPermissions, к сожалению, это не сработало. Я также попробовал то, что вы упомянули, и это тоже не сработало.
AndyOHart,

Это работало для меня в Android M. Но просто интересно, не вызовет ли это каких-либо проблем в устройствах Pre-M?
Бала Вишну

4
@goodgamerguy убедитесь, что вы не используете код запроса в onRequestPermissionsResult действия. если ваша деятельность также использует обратный вызов onRequestPermissionsResult, вы должны указать значение по умолчанию: super.onRequestPermissionsResult (requestCode, permissions, grantResults);
Тимми Саймонс

68

Я надеюсь, что это работает нормально

Для деятельности:

 ActivityCompat.requestPermissions(this,permissionsList,REQUEST_CODE);

Для фрагмента:

 requestPermissions(permissionsList,REQUEST_CODE);

Как насчет SDK 15 и ниже?
zulkarnain shah

4
Вам не нужно разрешение на SDK 15, нужно только Marshmallow и более
поздняя

50

Я тоже столкнулся с этой проблемой. Если вы хотите, чтобы действие, которое обрабатывает разрешения, отсутствовало в истории / событиях, то у вас возникнет соблазн изменить свою AndroidManifest.xmlзапись.

Если вы установите активность, которую вы вызываете requestPermissionsили AppCompatActivity.requestPermissionsс

android:noHistory="true"
android:excludeFromRecents="true"

по твоему AndroidManifest.xmlтогда onRequestPermissionsResult()не назовешь. Это верно, если ваша активность получена из Activityили AppCompatActivity.

Это можно исправить, удалив оба флага из AndroidManifest.xml и завершив finishAndRemoveTask()вместо этого свою деятельность .


Я бы предпочел не удалять атрибут noHistory, но это решение помогло мне. Спасибо!
Эрик Шленц

1
У меня была проблема с Зефиром, но не с Нугой, спасибо, что это сработало для меня
Йохан Дахмани

37

Если у вас есть onRequestPermissionsResultактивность и фрагмент, обязательно позвоните super.onRequestPermissionsResultв активность. Фрагмент не требуется, но есть в действии.


Кроме того, обратите внимание, что если вы нацелены на API (например, на 19, например), вы все равно будете находиться под старой системой моделей разрешений и, следовательно, не будете получать вызовы функций, связанных с запросом разрешений
Bonatti

см. мой ответ, если вы продлили еще одно действие, которое не вызывает super
TouchBoarder

3
Наткнулся на этот. FragmentActivity.onRequestPermissionsResult(..)где вызов фрагментаonRequestPermissionsResult(..) перенаправляется .
JanPl

@Bonatti Так что, если я попытаюсь запросить разрешение в Android 4.4.2, я не получу результат? Таким образом, я не могу изменить разрешения, только увидеть их?
Алекс Фрагоцис

@AlexanderFragotsis Вы получите результат. Разница лишь в том, что вас не попросят дать разрешение, вместо этого система будет автоматически предоставлена ​​вам.
Душан Живкович

33

Ответ не рекомендуется, пожалуйста, обратитесь к: https://developer.android.com/training/permissions/requesting

Внутри фрагмента нужно позвонить:

FragmentCompat.requestPermissions(permissionsList, RequestCode)

Не:

ActivityCompat.requestPermissions(Activity, permissionsList, RequestCode);

2
Относится к android.support.v13.app.FragmentCompat
Уди Оши

1
FragmentCompat устарела с API 27.1.0.
Big McLargeHuge

мой ответ устарел, пожалуйста, обратитесь к: developer.android.com/training/permissions/requesting
Ben.Slama.Jihed

15

Если вы используете requestPermissions во фрагменте, он принимает 2 параметра вместо 3.

Вы должны использовать requestPermissions(permissions, PERMISSIONS_CODE);


1
На самом деле это дает ответ на вопрос. При вызове requestPermissions (метод экземпляра фрагмента) вместо ActivityCompat.requestPermissions вызывается метод результата. Который является главной проблемой здесь. Спасибо, Дон
Ваклок

15

Если по какой-то причине вы расширили пользовательское действие, расположенное в какой-либо внешней библиотеке, которая не вызывает super, вам нужно вручную вызвать Fragment super.onRequestPermissionsResult в своей активности onRequestPermissionsResult.

YourActivity extends SomeActivityNotCallingSuperOnRequestPermissionsResult{
Fragment requestingFragment;//the fragment requesting the permission
...
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestingFragment!=null)
            requestingFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
...

12

У вас есть функция checkPermissions для устройств до Зефира во FragmentCompat . Я использую так:

FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

2
Это работает, только если разрешение запрашивается внутри android.app.fragment, и вам нужно импортировать библиотеку support-v13!
МохаммадРеза

4
FragmentCompat.requestPermissions () не работает с android.support.v4.app.Fragment. Кто-нибудь знает, как справиться с этим.
Ютикорн

12

Я выяснил, что важно, когда вы звоните android.support.v4.app.Fragment.requestPermissions .

Если вы делаете это в onCreate(),onRequestPermissionsResult() никогда не вызывается.

Решение: позвоните onActivityCreated();

@Override
public void onActivityCreated(Bundle savedInstanceState) {

    super.onActivityCreated(savedInstanceState);

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED) 
        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 0);

}

1
Я сталкиваюсь с той же проблемой, но не могу получить onActivityCreated()в своем коде. SDK 23.
KAmol


Я не фрагмент, я пытаюсь сделать это на создание деятельности
kAmol

Хм или пользуетесь onPause()?
Almisoft

10

Эта проблема на самом деле была вызвана NestedFragments. В основном большинство фрагментов мы расширяем HostedFragment, который, в свою очередь, расширяет CompatFragment. Наличие этих вложенных фрагментов вызывало проблемы, которые в конечном итоге были решены другим разработчиком проекта.

Он делал некоторые вещи низкого уровня, такие как переключение битов, чтобы заставить это работать, поэтому я не слишком уверен в фактическом окончательном решении


Это помогло мне решить мою проблему: мне пришлось перенаправить onRequestPermissionsResult из родительского фрагмента в дочерний (вложенный) фрагмент. Спасибо за чаевые!
Мерфи

9
 /* use the object of your fragment to call the 
 * onRequestPermissionsResult in fragment after
 * in activity and use different request Code for 
 * both Activity and Fragment */

   if (isFragment)
    mFragment.requestPermissions(permissions.toArray(new
    String[permissions.size()]),requestPermission);

   else
    ActivityCompat.requestPermissions(mActivity,permissions.toArray(new
    String[permissions.size()]),requestPermission);

6

Это будет работать ..

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

супер реализация вызывает реализацию фрагментов, а не фреймворк
Мохаммад Яхья,

5

Если вы вызываете этот код из фрагмента, у него есть собственный метод requestPermissions.

Итак, основная концепция такова: если вы находитесь в Деятельности, то позвоните

ActivityCompat.requestPermissions(this,
                            permissionsList,
                            permissionscode);

и если во фрагменте, просто позвоните

requestPermissions(permissionsList,
                            permissionscode);

1
Требуется уровень API 23 ... На более ранних версиях FragmentCompat.requestPermissions().
Минас Мина

5

У меня была похожая проблема, за исключением того, что я нажимал кнопку, чтобы позвонить, что вызывает callIntent. Сначала я проверил разрешение, если не предоставлено, я запрашиваю разрешение, и onRequestPermissionResult я вызываю разрешение на проверку и снова звоню.

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case Constants.PERMISSIONS_REQUEST_CALL_PHONE: {
            if ( grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                checkPermissionsAndCall();
            }
        }
    }
}

public void checkPermissionsAndCall(){
    if (Build.VERSION.SDK_INT > 22) {
        if(ContextCompat.checkSelfPermission(getContext(),
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED){
            requestPermissions( new String[]{Manifest.permission.CALL_PHONE}, Constants.PERMISSIONS_REQUEST_CALL_PHONE);
        }
        else{
            callIntent();
        }
    }
}


2

Прежде чем проверять все согласно приведенным выше ответам, убедитесь, что ваш код запроса не равен 0 !!!

проверьте код onRequestPermissionsResult () в FragmentActivity.java:

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    int index = (requestCode>>16)&0xffff;
    if (index != 0) {
        index--;

        String who = mPendingFragmentActivityResults.get(index);
        mPendingFragmentActivityResults.remove(index);
        if (who == null) {
            Log.w(TAG, "Activity result delivered for unknown Fragment.");
            return;
        }
        Fragment frag = mFragments.findFragmentByWho(who);
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for who: " + who);
        } else {
            frag.onRequestPermissionsResult(requestCode&0xffff, permissions, grantResults);
        }
    }
}

2
private void showContacts() {
    if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
    } else {
        doShowContacts();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        doShowContacts();
    }
}

2

У меня есть удивительное решение для достижения этой цели сделать BaseActivity, как это.

public class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this));


}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case 1: {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                CustomToast.getInstance().setCustomToast("Now you can share the Hack.");

            } else {
                Toast.makeText(this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

}

Теперь вы можете вызвать код, чтобы запросить разрешение, как это

 ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);

Теперь каждый раз, когда это происходит где-либо, будь то в вашем фрагменте или в любом действии, будет вызываться базовое действие.

Спасибо


1

Вы можете использовать requestPermissions(PERMISSIONS, MULTIPLE_PERMISSIONS_CODE);. Не используйте FragmentCompat, если вы используете v4.


Пожалуйста, опишите почему.
Размещается

1

подробности

  • Котлин 1.2.70
  • проверено в minSdkVersion 19
  • Android студия 3.1.4

Алгоритм

модуль - камера, локация, ....

  1. Проверьте, имеет ли hasSystemFeature (если модуль существует в телефоне)
  2. Проверьте, есть ли у пользователя доступ к модулю
  3. Отправить запрос на разрешение (попросить пользователя разрешить использование модуля )

Характеристики

  1. Работа с действиями и фрагментами
  2. Имеет только один результат ответа
  3. Можно проверить несколько модулей в одном запросе

Решение

class PermissionType(val manifest_permission: String, val packageManager: String) {
    object Defined {
        val camera = PermissionType(Manifest.permission.CAMERA, PackageManager.FEATURE_CAMERA)
        val currentLocation = PermissionType(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.FEATURE_LOCATION_GPS)
    }
}

class  Permission {

    enum class PermissionResult {
        ACCESS_ALLOWED, ACCESS_DENIED, NO_SYSTEM_FEATURE;
    }

    interface ManagerDelegate {
        fun permissionManagerDelegate(result: Array<Pair<String, PermissionResult>>)
    }

    class Manager internal constructor(private val delegate: ManagerDelegate?) {

        private var context: Context? = null
        private var fragment: Fragment? = null
        private var activity: AppCompatActivity? = null
        private var permissionTypes: Array<PermissionType> = arrayOf()
        private val REQUEST_CODE = 999
        private val semaphore = Semaphore(1, true)
        private var result: Array<Pair<String, PermissionResult>> = arrayOf()


        constructor(permissionType: PermissionType, delegate: ManagerDelegate?): this(delegate) {
            permissionTypes = arrayOf(permissionType)
        }

        constructor(permissionTypes: Array<PermissionType>, delegate: ManagerDelegate?): this(delegate) {
            this.permissionTypes = permissionTypes
        }

        init {
            when (delegate) {
                is Fragment -> {
                    this.fragment = delegate
                    this.context = delegate.context
                }

                is AppCompatActivity -> {
                    this.activity = delegate
                    this.context = delegate
                }
            }
        }

        private fun hasSystemFeature(permissionType: PermissionType) : Boolean {
            return context?.packageManager?.hasSystemFeature(permissionType.packageManager) ?: false
        }

        private fun hasAccess(permissionType: PermissionType) : Boolean {
            return if (Build.VERSION.SDK_INT < 23) true else {
                context?.checkSelfPermission(permissionType.manifest_permission) == PackageManager.PERMISSION_GRANTED
            }
        }

        private fun sendRequest(permissionTypes: Array<String>) {

            if (fragment != null) {
                fragment?.requestPermissions(permissionTypes, REQUEST_CODE)
                return
            }

            if (activity != null){
                ActivityCompat.requestPermissions(activity!!, permissionTypes, REQUEST_CODE)
            }
        }

        fun check() {

            semaphore.acquire()
            AsyncTask.execute {
                var permissionsForSendingRequest: Array<String> = arrayOf()
                this.result = arrayOf()

                for (it in permissionTypes) {
                    if (!hasSystemFeature(it)) {
                        result += Pair(it.manifest_permission, PermissionResult.NO_SYSTEM_FEATURE)
                        continue
                    }

                    if (hasAccess(it)) {
                        result += Pair(it.manifest_permission, PermissionResult.ACCESS_ALLOWED)
                    } else {
                        permissionsForSendingRequest += it.manifest_permission
                    }
                }

                if (permissionsForSendingRequest.isNotEmpty()) {
                    sendRequest(permissionsForSendingRequest)
                } else {
                    delegate?.permissionManagerDelegate(result)
                }
            }
        }

        fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
            when (requestCode) {
                REQUEST_CODE -> {
                    if (grantResults.isEmpty()) {
                        return
                    }
                    for ((i,permission) in permissions.withIndex()) {
                        for (item in this.permissionTypes) {
                            if (permission == item.manifest_permission && i < grantResults.size) {
                                result += if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                                    Pair(item.manifest_permission, PermissionResult.ACCESS_ALLOWED)
                                } else {
                                    Pair(item.manifest_permission, PermissionResult.ACCESS_DENIED)
                                }
                                break
                            }
                        }
                    }
                    delegate?.permissionManagerDelegate(result)
                }
            }
            semaphore.release()
        }
    }
}

Использование в Activity (фрагмент тот же)

class BaseActivity : AppCompatActivity(), Permission.ManagerDelegate {

    private lateinit var permissionManager: Permission.Manager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.base_activity)

        permissionManager = Permission.Manager(arrayOf(PermissionType.Defined.camera, PermissionType.Defined.currentLocation), this)
        permissionManager.check()
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        permissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }


    override fun permissionManagerDelegate(result: Array<Pair<String, Permission.PermissionResult>>) {
        result.forEach {
            println("!!! ${it.first} ${it.second}")
//            when (it.second) {
//                Permission.PermissionResult.NO_SYSTEM_FEATURE -> {
//                }
//
//                Permission.PermissionResult.ACCESS_DENIED  -> {
//                }
//
//                Permission.PermissionResult.ACCESS_ALLOWED -> {
//                }
//            }
        }
    }
}

1

Здесь я хочу показать свой код, как мне это удалось.

public class CheckPermission {

public Context context;

public static final int PERMISSION_REQUEST_CODE =  200;

public CheckPermission(Context context){
    this.context = context;
}

public boolean isPermissionGranted(){
    int read_contact = ContextCompat.checkSelfPermission(context.getApplicationContext() , READ_CONTACTS);
    int phone = ContextCompat.checkSelfPermission(context.getApplicationContext() , CALL_PHONE);

    return read_contact == PackageManager.PERMISSION_GRANTED && phone == PackageManager.PERMISSION_GRANTED;
   }
}

Здесь, в этом классе, я хочу проверить, разрешено или нет. Не тогда я буду называть разрешение от моей MainActivity, как

public void requestForPermission() {
       ActivityCompat.requestPermissions(MainActivity.this, new String[]    {READ_CONTACTS, CALL_PHONE}, PERMISSION_REQUEST_CODE);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
                    showMessageOKCancel("You need to allow access to both the permissions",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE},
                                                PERMISSION_REQUEST_CODE);
                                    }
                                }
                            });
                    return;
                }
            }
    }
}

Теперь в методе onCreate вам нужно вызвать функцию requestForPermission ().

Вот и все. Также вы можете запросить несколько разрешений одновременно.


1

ОБНОВЛЕНИЕ: видел чей-то ответ о вызове super.onRequestPermissionResult()в Деятельности, и это устраняет проблему кода запроса, которую я упомянул, и вызывает фрагмент onRequestPermissionResult.

игнорировать этот материал:

Для меня это был вызов onRequestPermissionResultActivity, несмотря на мой вызов fragment.requestPermission(...), НО он вернул результат с неправильным кодом запроса (111 превратился в 65647, почему ??? Я никогда не узнаю).

К счастью, это единственное разрешение, которое мы запрашиваем на этом экране, поэтому я просто игнорирую код запроса (у меня нет времени, чтобы выяснить, почему это не правильно сейчас)


Добавьте 65536 и 111, и вы получите это. Это из-за битовых масок. Смотрите некоторые темы.
CoolMind

0

Я также столкнулся с проблемой, что даже если вы вызываете правильный requestPermissions, вы все равно можете иметь эту проблему. Проблема в том, что родительская активность может переопределить этот метод без вызова super. Добавьте супер, и это будет исправлено.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.